Create SAML response based on SAML request - java

I have developed a Java web application, and I want to implement SAML. These are the steps I believe is right to implement SAML.
The Service Provider(SP, my application in this case) sends a SAML authentication request to IdP.
The IdP then validates it and create a SAML response assertion and signs it with the certificate, and send back to SP.
SP then validates it with public key of certificate in keystore, and proceeds further based on that.
I have got a sample code and I am able to create SAML request and its like this
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_c7b796f4-bc16-4fcc-8c1d-36befffc39c2" Version="2.0"
IssueInstant="2014-10-30T11:21:08Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
AssertionConsumerServiceURL="http://localhost:8080/mywebapp/consume.jsp">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8080/mywebapp
</saml:Issuer>
<samlp:NameIDPolicy
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified"
AllowCreate="true"></samlp:NameIDPolicy>
<samlp:RequestedAuthnContext Comparison="exact">
<saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
I can encode it and send to IdP.
I want to create sample Java code to get this SAML request and then create a SAML response.
How can I decode the request and validate it and create response? And Do I need to sign the saml response with certificate? and then send back to the SP?
Thanks.

Although this is an old post, I am adding sample code and references which I found useful.
SAMLResponse = hreq.getParameter("SAMLResponse");
InputSource inputSource = new InputSource(new StringReader(SAMLResponse));
SAMLReader samlReader = new SAMLReader();
response2 = org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);
Now Validate the digital signature :
org.opensaml.saml2.core.Response response2 = (org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);
//To fetch the digital signature from the response.
Signature signature = response2.getSignature();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(domainName);
//pull out the public key part of the certificate into a KeySpec
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
//get KeyFactory object that creates key objects, specifying RSA - java.security.KeyFactory
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key to validate signatures
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//we have the public key
BasicX509Credential publicCredential = new BasicX509Credential();
//add public key value
publicCredential.setPublicKey(publicKey);
//create SignatureValidator
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
//try to validate
try{
signatureValidator.validate(signature);
catch(Exception e){
//
}
Now fetch the assertion map :
samlDetailsMap = setSAMLDetails(response2);
In the above logic use the below private method to pull all the assertion attributes. Finally you will have map of all the fields that are sent to you.
private Map<String, String> setSAMLDetails(org.opensaml.saml2.core.Response response2){
Map<String, String> samlDetailsMap = new HashMap<String, String>();
try {
List<Assertion> assertions = response2.getAssertions();
LOGGER.error("No of assertions : "+assertions.size());
for(Assertion assertion:assertions){
List<AttributeStatement> attributeStatements = assertion.getAttributeStatements();
for(AttributeStatement attributeStatement: attributeStatements){
List<Attribute> attributes = attributeStatement.getAttributes();
for(Attribute attribute: attributes){
String name = attribute.getName();
List<XMLObject> attributes1 = attribute.getAttributeValues();
for(XMLObject xmlObject : attributes1){
if(xmlObject instanceof XSString){
samlDetailsMap.put(name, ((XSString) xmlObject).getValue());
LOGGER.error("Name is : "+name+" value is : "+((XSString) xmlObject).getValue());
}else if(xmlObject instanceof XSAnyImpl){
String value = ((XSAnyImpl) xmlObject).getTextContent();
samlDetailsMap.put(name, value);
}
}
}
}
}
} catch (Exception e) {
LOGGER.error("Exception occurred while setting the saml details");
}
LOGGER.error("Exiting from setSAMLDetails method");
return samlDetailsMap;
}
Add new class SAMLReader as below :
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.opensaml.DefaultBootstrap;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.UnmarshallingException;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class SAMLReader {
private static DocumentBuilder builder;
static{
try{
DefaultBootstrap.bootstrap ();
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance ();
factory.setNamespaceAware (true);
builder = factory.newDocumentBuilder ();
}catch (Exception ex){
ex.printStackTrace ();
}
}
/**
*
* #param filename
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public XMLObject readFromFile (String filename)
throws IOException, UnmarshallingException, SAXException{
return fromElement (builder.parse (filename).getDocumentElement ());
}
/**
*
* #param is
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public XMLObject readFromFile (InputStream is)
throws IOException, UnmarshallingException, SAXException{
return fromElement (builder.parse (is).getDocumentElement ());
}
/**
*
* #param is
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public XMLObject readFromFile (InputSource is)
throws IOException, UnmarshallingException, SAXException{
return fromElement (builder.parse (is).getDocumentElement ());
}
/**
*
* #param element
* #return
* #throws IOException
* #throws UnmarshallingException
* #throws SAXException
*/
public static XMLObject fromElement (Element element)
throws IOException, UnmarshallingException, SAXException{
return Configuration.getUnmarshallerFactory ()
.getUnmarshaller (element).unmarshall (element);
}
}

The steps you've listed are more or less correct. The only thing I'd point to is that you have to be careful with the meaning if the word sends (ex. in "SP ... sends a SAML authentication request to IdP"). SAML allows authentications scenarios with zero direct communication between SP and IdP.
Another small addition is that SP may also sign his request, so you may have signature validation on both sides. Validation on the SP side is obligatory.
If you want to implement SAML, you may want to check one of the existing solutions, for example Shibboleth. If you're on platforms like Spring and JBoss you may want to check Spring Security SAML or JBoss PicketLink. If you want to go lower-level, check OpenSAML.
In my corp we have JBoss as standard and are very happy with PicketLink.

Related

How to enable Long Term Validation (LTV) with pdfbox

I using pdfbox to signature but when check signature in acrobat reader has result: Long term validation(LTV) not enable
And this is my source code
#Override
public byte[] sign(InputStream content) throws IOException {
try {
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
X509Certificate cert = (X509Certificate) this.certificateChain[0];
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(this.privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, cert));
gen.addCertificates(new JcaCertStore(Arrays.asList(this.certificateChain)));
CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
CMSSignedData signedData = gen.generate(msg, false);
//add timestamp if TSA is available
TimeStampManager timeStampManager = new TimeStampManager();
signedData = timeStampManager.addSignedTimeStamp(signedData, timeStampToken);
return signedData.getEncoded();
} catch (Exception e) {
// Write log error sign to table Log in DB
// TODO: 10/19/20
//throw new IOException cause a SignatureInterface
throw new IOException(e);
}
}
TimestampManager.addSignedTimeStamp
/**
* Extend cms signed data with TimeStamp first or to all signers
*
* #param signedData Generated CMS signed data
* #param timeStampToken TimeStampToken
* #return CMSSignedData Extended CMS signed data
* #throws IOException, TSPException
*/
public CMSSignedData addSignedTimeStamp(CMSSignedData signedData, TimeStampToken timeStampToken) throws IOException, TSPException {
SignerInformationStore signerStore = signedData.getSignerInfos();
List<SignerInformation> signersWithTimeStamp = new ArrayList<>();
for (SignerInformation signer : signerStore.getSigners()) {
// This adds a timestamp to every signer (into his unsigned attributes) in the signature.
signersWithTimeStamp.add(signTimeStamp(signer, timeStampToken));
}
// new SignerInformationStore have to be created cause new SignerInformation instance
// also SignerInformationStore have to be replaced in a signedData
return CMSSignedData.replaceSigners(signedData, new SignerInformationStore(signersWithTimeStamp));
}
/**
* Extend CMS Signer Information with the TimeStampToken into the unsigned Attributes.
*
* #param signer information about signer
* #return information about SignerInformation
* #throws IOException
*/
private SignerInformation signTimeStamp(SignerInformation signer, TimeStampToken timeStampToken) throws IOException, TSPException {
AttributeTable unsignedAttributes = signer.getUnsignedAttributes();
ASN1EncodableVector vector = new ASN1EncodableVector();
if (unsignedAttributes != null) {
vector = unsignedAttributes.toASN1EncodableVector();
}
byte[] token = timeStampToken.getEncoded();
ASN1ObjectIdentifier oid = PKCSObjectIdentifiers.id_aa_signatureTimeStampToken;
ASN1Encodable signatureTimeStamp = new Attribute(oid, new DERSet(ASN1Primitive.fromByteArray(token)));
vector.add(signatureTimeStamp);
Attributes signedAttributes = new Attributes(vector);
// replace unsignedAttributes with the signed once
return SignerInformation.replaceUnsignedAttributes(signer, new AttributeTable(signedAttributes));
}
I want signature auto enable LTV same this
Please help auto enable LTV in signature with pdfbox in my source code!
Thanks!
Update:
My issue when i use pdf-example version 2.0.21
Then i update version pdf-example to 2.0.23 then my issue is resolve

Removing XML declaration in JAX-WS message

I'm trying to invoke a webservice using Java code.
So I used JAX-WS and JAXB to generate my object from wsdl file.
When I invoke the webservice it respond with this error:
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: The [javax.xml.transform.TransformerException] occurred during XSLT transformation: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: The XML declaration must end with "?>".
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: The [javax.xml.transform.TransformerException] occurred during XSLT transformation: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: The XML declaration must end with "?>".
at com.sun.xml.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:189)
at com.sun.xml.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:122)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:118)
So with wireshark I analysed the xml message that is being sent. And tried to resend it with soapUI.
And found out that my xml contains the xml declaration
<?xml version='1.0' encoding='UTF-8'?>
When I remove it from SoapUI and resend it. The message goes ok.
My java code goes like this:
public static Data receiveSIBS(webserviceclient.Data input) {
webserviceclient.Starter service = new webserviceclient.Starter();
webserviceclient.PortType port = service.getSOAPEventSource();
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
return port.receiveSIBS(input);
}
How can I generate my message in Java without this xml declaration?
because the xml message is all generated with JAX-WS and JAXB.
Thanks in advanced!
Found my own solution!
First, as referred in other post, I implemented a SOAPHandler to edit this two properties:
soapMsg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-16");
soapMsg.setProperty(SOAPMessage.WRITE_XML_DECLARATION, "false");
But although this two properties change message instance inside handleMessage() method, it won't be sent like it, and message with default xml declaration is sent.
Instead of setting this properties the solution was to set this two NamespaceDeclaration:
SOAPEnvelope env = sp.getEnvelope();
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
I don't understand why we get "The XML declaration must end with "?>"" error. Because my solution didn't removed xml declaration. Might be related to xml structure (but I don't have enough knowledge to confirm it).
I need to refer http://blog.jdevelop.eu/?p=67 post that let me to this solution, and some debug code is from this post.
Following I put my complete CustomHandler class so it can held anyone.
import java.io.ByteArrayOutputStream;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/**
*
* #author Daniel Chang Yan
*/
public class CustomHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext context) {
Boolean isOutbound
= (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (isOutbound != null && isOutbound) {
SOAPMessage soapMsg = context.getMessage();
try {
//Properties always rewritten by jaxws, no matter what is set here
//soapMsg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-16");
//soapMsg.setProperty(SOAPMessage.WRITE_XML_DECLARATION, "false");
// get SOAP-Part
SOAPPart sp = soapMsg.getSOAPPart();
//edit Envelope
SOAPEnvelope env = sp.getEnvelope();
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
} catch (SOAPException e) {
throw new RuntimeException(e);
}
// print SOAP-Message
System.out.println("Direction=outbound (handleMessage)...");
dumpSOAPMessage(soapMsg);
} else {
// INBOUND
System.out.println("Direction=inbound (handleMessage)...");
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
}
return true;
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleFault(SOAPMessageContext context) {
System.out.println("ServerSOAPHandler.handleFault");
boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound (handleFault)...");
} else {
System.out.println("Direction=inbound (handleFault)...");
}
if (!outbound) {
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
if (context.getMessage().getSOAPBody().getFault() != null) {
String detailName = null;
try {
detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
System.out.println("detailName=" + detailName);
} catch (Exception e) {
}
}
} catch (SOAPException e) {
e.printStackTrace();
}
}
return true;
}
public void close(MessageContext mc) {
}
/**
* Dump SOAP Message to console
*
* #param msg
*/
private void dumpSOAPMessage(SOAPMessage msg) {
if (msg == null) {
System.out.println("SOAP Message is null");
return;
}
//System.out.println("");
System.out.println("--------------------");
System.out.println("DUMP OF SOAP MESSAGE");
System.out.println("--------------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
System.out.println(baos.toString(getMessageEncoding(msg)));
// show included values
String values = msg.getSOAPBody().getTextContent();
System.out.println("Included values:" + values);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns the message encoding (e.g. utf-8)
*
* #param msg
* #return
* #throws javax.xml.soap.SOAPException
*/
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING).toString();
}
return encoding;
}
}

JWT (JSON Web Token) library for Java [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
I am working on a web application developed using Java and AngularJS and chose to implement token authentication and authorization.
For the exercise purpose, I've come to the point where I send the credentials to the server, generate a random token store it and send it back to the client.
At every request to the server I'm attaching the token in the header and it works perfectly.
For the authentication point of view is perfect and wouldn't need more.
However, I now want to keep track of the user type (admin, regular user...), as well as it's id, or any other unique field; as I understood I have to encrypt that in the token that I'm sending back to the client during the log in action. Is that correct?
Is there any JWT library that you used and can generate, encrypt and decrypt such tokens?
A link to the library's API and Maven dependency would be much appreciated.
Thanks
JJWT aims to be the easiest to use and understand JWT library for the JVM and Android:
https://github.com/jwtk/jjwt
If anyone in the need for an answer,
I used this library: http://connect2id.com/products/nimbus-jose-jwt
Maven here: http://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt/2.10.1
By referring to https://jwt.io/ you can find jwt implementations in many languages including java. Also the site provide some comparison between these implementation (the algorithms they support and ....).
For java these are mentioned libraries:
https://github.com/jwtk/jjwt
https://github.com/auth0/java-jwt ( A tutorial at https://auth0.com/docs/server-apis/java)
https://bitbucket.org/b_c/jose4j
https://bitbucket.org/connect2id/nimbus-jose-jwt
This library seems to work well: https://code.google.com/p/jsontoken/ .
It depends on Google Guava. Here are the Maven artifacts:
<dependency>
<groupId>com.googlecode.jsontoken</groupId>
<artifactId>jsontoken</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
The library is in fact used by Google Wallet.
Here is how to create a jwt, and to verify it and deserialize it:
import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Calendar;
import java.util.List;
import net.oauth.jsontoken.JsonToken;
import net.oauth.jsontoken.JsonTokenParser;
import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.crypto.HmacSHA256Verifier;
import net.oauth.jsontoken.crypto.SignatureAlgorithm;
import net.oauth.jsontoken.crypto.Verifier;
import net.oauth.jsontoken.discovery.VerifierProvider;
import net.oauth.jsontoken.discovery.VerifierProviders;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
import com.google.common.collect.Lists;
import com.google.gson.JsonObject;
/**
* Provides static methods for creating and verifying access tokens and such.
* #author davidm
*
*/
public class AuthHelper {
private static final String AUDIENCE = "NotReallyImportant";
private static final String ISSUER = "YourCompanyOrAppNameHere";
private static final String SIGNING_KEY = "LongAndHardToGuessValueWithSpecialCharacters#^($%*$%";
/**
* Creates a json web token which is a digitally signed token that contains a payload (e.g. userId to identify
* the user). The signing key is secret. That ensures that the token is authentic and has not been modified.
* Using a jwt eliminates the need to store authentication session information in a database.
* #param userId
* #param durationDays
* #return
*/
public static String createJsonWebToken(String userId, Long durationDays) {
//Current time and signing algorithm
Calendar cal = Calendar.getInstance();
HmacSHA256Signer signer;
try {
signer = new HmacSHA256Signer(ISSUER, null, SIGNING_KEY.getBytes());
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
//Configure JSON token
JsonToken token = new net.oauth.jsontoken.JsonToken(signer);
token.setAudience(AUDIENCE);
token.setIssuedAt(new org.joda.time.Instant(cal.getTimeInMillis()));
token.setExpiration(new org.joda.time.Instant(cal.getTimeInMillis() + 1000L * 60L * 60L * 24L * durationDays));
//Configure request object, which provides information of the item
JsonObject request = new JsonObject();
request.addProperty("userId", userId);
JsonObject payload = token.getPayloadAsJsonObject();
payload.add("info", request);
try {
return token.serializeAndSign();
} catch (SignatureException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies a json web token's validity and extracts the user id and other information from it.
* #param token
* #return
* #throws SignatureException
* #throws InvalidKeyException
*/
public static TokenInfo verifyToken(String token)
{
try {
final Verifier hmacVerifier = new HmacSHA256Verifier(SIGNING_KEY.getBytes());
VerifierProvider hmacLocator = new VerifierProvider() {
#Override
public List<Verifier> findVerifier(String id, String key){
return Lists.newArrayList(hmacVerifier);
}
};
VerifierProviders locators = new VerifierProviders();
locators.setVerifierProvider(SignatureAlgorithm.HS256, hmacLocator);
net.oauth.jsontoken.Checker checker = new net.oauth.jsontoken.Checker(){
#Override
public void check(JsonObject payload) throws SignatureException {
// don't throw - allow anything
}
};
//Ignore Audience does not mean that the Signature is ignored
JsonTokenParser parser = new JsonTokenParser(locators,
checker);
JsonToken jt;
try {
jt = parser.verifyAndDeserialize(token);
} catch (SignatureException e) {
throw new RuntimeException(e);
}
JsonObject payload = jt.getPayloadAsJsonObject();
TokenInfo t = new TokenInfo();
String issuer = payload.getAsJsonPrimitive("iss").getAsString();
String userIdString = payload.getAsJsonObject("info").getAsJsonPrimitive("userId").getAsString();
if (issuer.equals(ISSUER) && !StringUtils.isBlank(userIdString))
{
t.setUserId(new ObjectId(userIdString));
t.setIssued(new DateTime(payload.getAsJsonPrimitive("iat").getAsLong()));
t.setExpires(new DateTime(payload.getAsJsonPrimitive("exp").getAsLong()));
return t;
}
else
{
return null;
}
} catch (InvalidKeyException e1) {
throw new RuntimeException(e1);
}
}
}
public class TokenInfo {
private ObjectId userId;
private DateTime issued;
private DateTime expires;
public ObjectId getUserId() {
return userId;
}
public void setUserId(ObjectId userId) {
this.userId = userId;
}
public DateTime getIssued() {
return issued;
}
public void setIssued(DateTime issued) {
this.issued = issued;
}
public DateTime getExpires() {
return expires;
}
public void setExpires(DateTime expires) {
this.expires = expires;
}
}
This is based on code here: https://developers.google.com/wallet/instant-buy/about-jwts
And Here: https://code.google.com/p/wallet-online-sample-java/source/browse/src/com/google/wallet/online/jwt/util/WalletOnlineService.java?r=08b3333bd7260b20846d7d96d3cf15be8a128dfa
IETF has suggested jose libs on it's wiki:
http://trac.tools.ietf.org/wg/jose/trac/wiki
I would highly recommend using them for signing. I am not a Java guy, but seems like jose4j seems like a good option. Has nice examples as well: https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples
Update: jwt.io provides a neat comparison of several jwt related
libraries, and their features. A must check!
I would love to hear about what other java devs prefer.
I found this to be small and complete https://github.com/auth0/java-jwt
This page keeps references to implementations in various languages, including Java, and compares features: http://kjur.github.io/jsjws/index_mat.html
If you only need to parse unsigned unencrypted tokens you could use this code:
boolean parseJWT_2() {
String authToken = getToken();
String[] segments = authToken.split("\\.");
String base64String = segments[1];
int requiredLength = (int)(4 * Math.ceil(base64String.length() / 4.0));
int nbrPaddings = requiredLength - base64String.length();
if (nbrPaddings > 0) {
base64String = base64String + "====".substring(0, nbrPaddings);
}
base64String = base64String.replace("-", "+");
base64String = base64String.replace("_", "/");
try {
byte[] data = Base64.decode(base64String, Base64.DEFAULT);
String text;
text = new String(data, "UTF-8");
tokenInfo = new Gson().fromJson(text, TokenInfo.class);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
https://github.com/networknt/jsontoken
This is a fork of original google jsontoken
It has not been updated since Sep 11, 2012 and depends on some old packages.
What I have done:
Convert from Joda time to Java 8 time. So it requires Java 8.
Covert Json parser from Gson to Jackson as I don't want to include two Json parsers to my projects.
Remove google collections from dependency list as it is stopped long time ago.
Fix thread safe issue with Java Mac.doFinal call.
All existing unit tests passed along with some newly added test cases.
Here is a sample to generate token and verify the token. For more information, please check https://github.com/networknt/light source code for usage.
I am the author of both jsontoken and Omni-Channel Application Framework.

removing mustUnderstand attribute from soap headers

How to remove mustunderstand attribute from soap header in axis client.even i dont set it especially, when i set soap header info mustundertand and actor attributes are automatically added to soap message.Does anybody know how to remove them ?
I am using Axis2 1.4 version's wsdl2java to create my ws client.
None of those solutions worked for me, as:
I am working with Axis (not Axis2)
I don't want the attribute to be specified at all, as my server counterpart doesn't support it ("The 'http://schemas.xmlsoap.org/soap/envelope/:mustUnderstand' attribute is not declared" is a typical answer in such a case).
Looking at the answer to "Adding ws-security to wsdl2java generated classes" helped me to write a solution that worked for me:
void addSecurityHeader(Stub stub, final String username, final String password) {
QName headerName = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security"); // Or any other namespace that fits in your case
AtomicReference<SOAPHeaderElement> header
= new AtomicReference<SOAPHeaderElement>
(new SOAPHeaderElement(headerName) {
{
SOAPElement utElem = addChildElement("UsernameToken");
utElem.addChildElement("Username").setValue(username);
utElem.addChildElement("Password").setValue(password);
}
#Override
public void setAttribute(String namespace, String localName, String value) {
if (!Constants.ATTR_MUST_UNDERSTAND.equals(localName)) { // Or any other attribute name you'd want to avoid
super.setAttribute(namespace, localName, value);
}
}
});
SOAPHeaderElement soapHeaderElement = header.get();
soapHeaderElement.setActor(null); // No intermediate actors are involved.
stub.setHeader(soapHeaderElement); // Finally, attach the header to the stub
}
If you want to disable the must understand check in the AXIS client you have
to add the following line to your code:
_call.setProperty(Call.CHECK_MUST_UNDERSTAND, new Boolean(false));
then the MustUnderstandChecker of the AXIS Client is never invoked.
In my case it worked manually remove the attribute from the SOAPHeader
SOAPHeader header = env.getHeader();
OMChildrenQNameIterator childrenWithName = (OMChildrenQNameIterator) header.getChildrenWithName(omElementauthentication.getQName());
while (childrenWithName.hasNext()) {
org.apache.axiom.om.OMElement omElement = (org.apache.axiom.om.OMElement) childrenWithName.next();
QName mustAnderstandQName = omElement.resolveQName("soapenv:mustUnderstand");
if (mustAnderstandQName == null) {
continue;
}
OMAttribute mustAnderstandAttribute = omElement.getAttribute(mustAnderstandQName);
if (mustAnderstandAttribute == null) {
continue;
}
omElement.removeAttribute(mustAnderstandAttribute);
}
I was recently struggling with similar situation and by doing some google-ing I managed to find the following solution.
Having used Axis2 you would've probably generated a MyWSStub file that contains the calls to your service.
Create an wrapper class (better not touch the auto-generated stub files) that extends your stub e.g. MyWSStubWrapper:
public class MyWSStubWrapper extends MyWSStub {
/**
* #throws AxisFault
*/
public MyWSStubWrapper() throws AxisFault {
}
/**
* #param configurationContext
* #throws AxisFault
*/
public MyWSStubWrapper(ConfigurationContext configurationContext) throws AxisFault {
super(configurationContext);
}
/**
* #param targetEndpoint
* #throws AxisFault
*/
public MyWSStubWrapper(String targetEndpoint) throws AxisFault {
super(targetEndpoint);
}
/**
* #param configurationContext
* #param targetEndpoint
* #throws AxisFault
*/
public MyWSStubWrapper(ConfigurationContext configurationContext, String targetEndpoint) throws AxisFault {
super(configurationContext, targetEndpoint);
}
/**
* #param configurationContext
* #param targetEndpoint
* #param useSeparateListener
* #throws AxisFault
*/
public MyWSStubWrapper(ConfigurationContext configurationContext, String targetEndpoint, boolean useSeparateListener) throws AxisFault {
super(configurationContext, targetEndpoint, useSeparateListener);
}
#Override
protected void addHeader(OMElement omElementToadd, SOAPEnvelope envelop, boolean mustUnderstand) {
SOAPHeaderBlock soapHeaderBlock = envelop.getHeader().addHeaderBlock(omElementToadd.getLocalName(), omElementToadd.getNamespace());
OMNode omNode = null;
// add child elements
for (Iterator iter = omElementToadd.getChildren(); iter.hasNext();) {
omNode = (OMNode) iter.next();
iter.remove();
soapHeaderBlock.addChild(omNode);
}
OMAttribute omatribute = null;
// add attributes
for (Iterator iter = omElementToadd.getAllAttributes(); iter.hasNext();) {
omatribute = (OMAttribute) iter.next();
soapHeaderBlock.addAttribute(omatribute);
}
}
}
Bear in mind that the above code will completely remove the soapenv:mustUnderstand="0|1" from your headers if you wish to added you need to call soapHeaderBlock.setMustUnderstand(true|false); somewhere in your code.
1) Are you using the Axis SOAPHeaderElement and if so, are you setting the mustUnderstand setter to false?
2) Since you're generating your client with wsdl2java, it's quite possible that the WSDL (or more accurately, the schema) contains the mustUnderstand attribute on an element referenced in your SOAP binding. So when wsdlToJava generates the client code, those attributes will naturally be added. See here for a description of the mustUnderstand attribute.
If modifying the WSDL is out of the question, and you must remove this attribute from the header, then I suppose you can try to do it with a handler
3) Not advisable, but if you really MUST remove this attribute then I suppose you can add a client side handler that alters the header: http://ws.apache.org/axis/java/apiDocs/org/apache/axis/handlers/package-summary.html
i am using axis 1.4 client with ws security
in my case as Reinhard said
this worked
MyService service = new MyServiceLocator();
MyServicePortType port = service.getMyServiceHttpsSoap11Endpoint();
((Stub) port)._setProperty(Call.CHECK_MUST_UNDERSTAND, Boolean.FALSE);

How to obtain a kerberos service ticket via GSS-API?

Does anyone know how to get a service ticket from the Key Distribution Center (KDC) using the Java GSS-API?
I have a thick-client-application that first authenticates via JAAS using the Krb5LoginModule to fetch the TGT from the ticket cache (background: Windows e.g. uses a kerberos implementation and stores the ticket granting ticket in a secure memory area). From the LoginManager I get the Subject object which contains the TGT. Now I hoped when I create a specific GSSCredential object for my service, the service ticket will be put into the Subject's private credentials as well (I've read so somewhere in the web). So I have tried the following:
// Exception handling ommitted
LoginContext lc = new LoginContext("HelloEjbClient", new DialogCallbackHandler());
lc.login()
Subject.doAs(lc.getSubject(), new PrivilegedAction() {
public Object run() {
GSSManager manager = GSSManager.getInstance();
GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME);
GSSCredential clientCreds = manager.createCredential(clientName, 8 * 3600, createKerberosOid(), GSSCredential.INITIATE_ONLY);
GSSName serverName = manager.createName("myService#localhost", GSSName.NT_HOSTBASED_SERVICE);
manager.createCredential(serverName, GSSCredential.INDEFINITE_LIFETIME, createKerberosOid(), GSSCredential.INITIATE_ONLY);
return null;
}
private Oid createKerberosOid() {
return new Oid("1.2.840.113554.1.2.2");
}
});
Unfortunately I get a GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt).
My understanding of getting the service ticket was wrong. I do not need to get the credentials from the service - this is not possible on the client, because the client really doesn't have a TGT for the server and therefore doesn't have the rights to get the service credentials.
What's just missing here is to create a new GSSContext and to initialize it. The return value from this method contains the service ticket, if I have understood that correctly. Here is a working code example. It must be run in a PrivilegedAction on behalf of a logged in subject:
GSSManager manager = GSSManager.getInstance();
GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME);
GSSCredential clientCred = manager.createCredential(clientName,
8 * 3600,
createKerberosOid(),
GSSCredential.INITIATE_ONLY);
GSSName serverName = manager.createName("http#server", GSSName.NT_HOSTBASED_SERVICE);
GSSContext context = manager.createContext(serverName,
createKerberosOid(),
clientCred,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestConf(false);
context.requestInteg(true);
byte[] outToken = context.initSecContext(new byte[0], 0, 0);
System.out.println(new BASE64Encoder().encode(outToken));
context.dispose();
The outToken contains then contains the Service Ticket. However this is not the way the GSS-API was meant to be used. Its goal was to hide those details to the code, so it is better to establish a GSSContext using the GSS-API on both sides. Otherwise you really should know what you are doing because of potential security holes.
For more information read the Sun SSO tutorial with kerberos more carefully than I did.
EDIT:
Just forgot that I am using Windows XP with SP2. There is a new "feature" in this version of Windows that disallows using the TGT in the Windows RAM. You have to edit the registry to allow this. For more information have a look at the JGSS Troubleshooting page topic in case you experience a "KrbException: KDC has no support for encryption type (14)" like I did.
I had a lot of problems to use this code, but I have at least a solution. I post it here, perhaps it will help some of you...
/**
* Tool to retrieve a kerberos ticket. This one will not be stored in the windows ticket cache.
*/
public final class KerberosTicketRetriever
{
private final static Oid KERB_V5_OID;
private final static Oid KRB5_PRINCIPAL_NAME_OID;
static {
try
{
KERB_V5_OID = new Oid("1.2.840.113554.1.2.2");
KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1");
} catch (final GSSException ex)
{
throw new Error(ex);
}
}
/**
* Not to be instanciated
*/
private KerberosTicketRetriever() {};
/**
*
*/
private static class TicketCreatorAction implements PrivilegedAction
{
final String userPrincipal;
final String applicationPrincipal;
private StringBuffer outputBuffer;
/**
*
* #param userPrincipal p.ex. <tt>MuelleHA#MYFIRM.COM</tt>
* #param applicationPrincipal p.ex. <tt>HTTP/webserver.myfirm.com</tt>
*/
private TicketCreatorAction(final String userPrincipal, final String applicationPrincipal)
{
this.userPrincipal = userPrincipal;
this.applicationPrincipal = applicationPrincipal;
}
private void setOutputBuffer(final StringBuffer newOutputBuffer)
{
outputBuffer = newOutputBuffer;
}
/**
* Only calls {#link #createTicket()}
* #return <tt>null</tt>
*/
public Object run()
{
try
{
createTicket();
}
catch (final GSSException ex)
{
throw new Error(ex);
}
return null;
}
/**
*
* #throws GSSException
*/
private void createTicket () throws GSSException
{
final GSSManager manager = GSSManager.getInstance();
final GSSName clientName = manager.createName(userPrincipal, KRB5_PRINCIPAL_NAME_OID);
final GSSCredential clientCred = manager.createCredential(clientName,
8 * 3600,
KERB_V5_OID,
GSSCredential.INITIATE_ONLY);
final GSSName serverName = manager.createName(applicationPrincipal, KRB5_PRINCIPAL_NAME_OID);
final GSSContext context = manager.createContext(serverName,
KERB_V5_OID,
clientCred,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestConf(false);
context.requestInteg(true);
final byte[] outToken = context.initSecContext(new byte[0], 0, 0);
if (outputBuffer !=null)
{
outputBuffer.append(String.format("Src Name: %s\n", context.getSrcName()));
outputBuffer.append(String.format("Target : %s\n", context.getTargName()));
outputBuffer.append(new BASE64Encoder().encode(outToken));
outputBuffer.append("\n");
}
context.dispose();
}
}
/**
*
* #param realm p.ex. <tt>MYFIRM.COM</tt>
* #param kdc p.ex. <tt>kerbserver.myfirm.com</tt>
* #param applicationPrincipal cf. {#link #TicketCreatorAction(String, String)}
* #throws GSSException
* #throws LoginException
*/
static public String retrieveTicket(
final String realm,
final String kdc,
final String applicationPrincipal)
throws GSSException, LoginException
{
// create the jass-config-file
final File jaasConfFile;
try
{
jaasConfFile = File.createTempFile("jaas.conf", null);
final PrintStream bos = new PrintStream(new FileOutputStream(jaasConfFile));
bos.print(String.format(
"Krb5LoginContext { com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useTicketCache=true debug=true ; };"
));
bos.close();
jaasConfFile.deleteOnExit();
}
catch (final IOException ex)
{
throw new IOError(ex);
}
// set the properties
System.setProperty("java.security.krb5.realm", realm);
System.setProperty("java.security.krb5.kdc", kdc);
System.setProperty("java.security.auth.login.config",jaasConfFile.getAbsolutePath());
// get the Subject(), i.e. the current user under Windows
final Subject subject = new Subject();
final LoginContext lc = new LoginContext("Krb5LoginContext", subject, new DialogCallbackHandler());
lc.login();
// extract our principal
final Set<Principal> principalSet = subject.getPrincipals();
if (principalSet.size() != 1)
throw new AssertionError("No or several principals: " + principalSet);
final Principal userPrincipal = principalSet.iterator().next();
// now try to execute the SampleAction as the authenticated Subject
// action.run() without doAsPrivileged leads to
// No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)
final TicketCreatorAction action = new TicketCreatorAction(userPrincipal.getName(), applicationPrincipal);
final StringBuffer outputBuffer = new StringBuffer();
action.setOutputBuffer(outputBuffer);
Subject.doAsPrivileged(lc.getSubject(), action, null);
return outputBuffer.toString();
}
public static void main (final String args[]) throws Throwable
{
final String ticket = retrieveTicket("MYFIRM.COM", "kerbserver", "HTTP/webserver.myfirm.com");
System.out.println(ticket);
}
}

Categories