Hi All,
After long time I am posting some useful stuff to my blog.
In this post I am trying to write code for signign assertion which is generated by OpenSAML ans signing is aslo performed by OpenSAML API’s.
Generating SAML assertion code can be found at http://blog.keksrolle.de/2010/07/27/how-to-create-a-valid-saml-2-0-assertion-with-opensaml-for-java.html
The code below uses keystore and x509 certificate to sign assertion.
package com.example.test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableEntryException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.opensaml.Configuration; import org.opensaml.DefaultBootstrap; import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.impl.ResponseMarshaller; import org.opensaml.xml.ConfigurationException; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.security.SecurityConfiguration; import org.opensaml.xml.security.SecurityHelper; import org.opensaml.xml.security.credential.Credential; import org.opensaml.xml.security.x509.BasicX509Credential; import org.opensaml.xml.signature.Signature; import org.opensaml.xml.signature.SignatureException; import org.opensaml.xml.signature.Signer; import org.opensaml.xml.util.XMLHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; public class SignAssertion { private final static Logger logger = LoggerFactory.getLogger(SignAssertion.class); private static Credential signingCredential = null; final static Signature signature = null; final static String password = "secret"; final static String certificateAliasName = "selfsigned"; final static String fileName = "C:\\Users\\Narendra\\Desktop\\SAML\\idpcert.jks"; @SuppressWarnings("static-access") private void intializeCredentials() { KeyStore ks = null; FileInputStream fis = null; char[] password = this.password.toCharArray(); // Get Default Instance of KeyStore try { ks = KeyStore.getInstance(KeyStore.getDefaultType()); } catch (KeyStoreException e) { logger.error("Error while Intializing Keystore", e); } // Read Ketstore as file Input Stream try { fis = new FileInputStream(fileName); } catch (FileNotFoundException e) { logger.error("Unable to found KeyStore with the given keystoere name ::" + fileName, e); } // Load KeyStore try { ks.load(fis, password); } catch (NoSuchAlgorithmException e) { logger.error("Failed to Load the KeyStore:: ", e); } catch (CertificateException e) { logger.error("Failed to Load the KeyStore:: ", e); } catch (IOException e) { logger.error("Failed to Load the KeyStore:: ", e); } // Close InputFileStream try { fis.close(); } catch (IOException e) { logger.error("Failed to close file stream:: ", e); } // Get Private Key Entry From Certificate KeyStore.PrivateKeyEntry pkEntry = null; try { pkEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(this.certificateAliasName, new KeyStore.PasswordProtection( this.password.toCharArray())); } catch (NoSuchAlgorithmException e) { logger.error("Failed to Get Private Entry From the keystore:: " + this.fileName, e); } catch (UnrecoverableEntryException e) { logger.error("Failed to Get Private Entry From the keystore:: " + this.fileName, e); } catch (KeyStoreException e) { logger.error("Failed to Get Private Entry From the keystore:: " + this.fileName, e); } PrivateKey pk = pkEntry.getPrivateKey(); X509Certificate certificate = (X509Certificate) pkEntry.getCertificate(); BasicX509Credential credential = new BasicX509Credential(); credential.setEntityCertificate(certificate); credential.setPrivateKey(pk); signingCredential = credential; logger.info("Private Key" + pk.toString()); } public static void main(String args[]) throws Exception { SignAssertion sign = new SignAssertion(); sign.intializeCredentials(); Signature signature = null; try { DefaultBootstrap.bootstrap(); } catch (ConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } signature = (Signature) Configuration.getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME) .buildObject(Signature.DEFAULT_ELEMENT_NAME); signature.setSigningCredential(signingCredential); // This is also the default if a null SecurityConfiguration is specified SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration(); // If null this would result in the default KeyInfoGenerator being used String keyInfoGeneratorProfile = "XMLSignature"; try { SecurityHelper.prepareSignatureParams(signature, signingCredential, secConfig, null); } catch (SecurityException e) { e.printStackTrace(); } catch (org.opensaml.xml.security.SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } Response resp = SAMLWriter.getSamlAssertion(); resp.setSignature(signature); try { Configuration.getMarshallerFactory().getMarshaller(resp).marshall(resp); } catch (MarshallingException e) { e.printStackTrace(); } try { Signer.signObject(signature); } catch (SignatureException e) { e.printStackTrace(); } ResponseMarshaller marshaller = new ResponseMarshaller(); Element plain = marshaller.marshall(resp); // response.setSignature(sign); String samlResponse = XMLHelper.nodeToString(plain); logger.info("********************\n*\n***********::" + samlResponse); } }
#1 by Karri on May 23, 2012 - 12:38 PM
Why are marshalling the response twice?
#2 by Deepak Rosario on July 17, 2012 - 6:35 PM
Line no 165 : Response resp = SAMLWriter.getSamlAssertion();
SAMLWriter is not included in the above code. And the SAMLWriter mentioned in the post which has an external link does not have the function getSamlAssertion(). I tried incorporating the above code in the SAMLWriter mentioned. But not able to create digital signature in the assertion.
#3 by Jason513 on July 17, 2012 - 11:01 PM
Hi Deepak,
Looks like over the time the code present in the external site changed a bit.
By calling
it will generate a saml response. by tweaking the external code you can do that.
In the external code only assertion is generating. We also need to generate response i.e inside response we embeded this assertion.
Probably by this weeked I will post the completed code.
Regd,
Narendra
#4 by anonymous on November 1, 2012 - 9:01 PM
you can use:
Response resp = (Response) Configuration.getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME)
.buildObject(Response.DEFAULT_ELEMENT_NAME);
#5 by na on April 18, 2014 - 7:41 AM
did you get this code working
#6 by Ahmed on July 18, 2012 - 4:34 PM
Hey Thanks for this very good article , I have an issue her that and is empty Do you know the reason for this , I cann’t validate the response against cert file .
#7 by Jason513 on July 18, 2012 - 6:40 PM
Hi Ahmed,
Can you more elobarate your question.
If you are looking for sample code on how to perform signature verification on saml you can find that info at: http://kevnls.blogspot.in/2009/07/processing-saml-in-java-using-opensaml.html
In the above mentioned link example is given for SAML 1.1 with littilet bit code tweaking you can make it work for SAML2.0.
Below code snippet will do the magic of signature verification.
Make sure that you are performing validation aganist response not the assertion.
Regards,
Narendra
#8 by Sandeep on August 21, 2012 - 9:00 PM
I didn’t find “idpcert.jks” file in my system, where can I get that? Also is this self signing SAML assertion?
#9 by Jason513 on July 24, 2013 - 1:38 AM
Refer to the below comments please.
#10 by FauxPas on July 20, 2013 - 2:41 AM
Hi,
Can you please tell me from where I should download the jks file?
Thanks
#11 by FauxPas on July 23, 2013 - 11:14 PM
I found the answer myself. The keystore is actually present in our jdk. Check out this link: http://stackoverflow.com/questions/12061644/how-to-self-sign-opensaml-assertion-for-the-version-saml-2-0-using-java
#12 by Jason513 on July 24, 2013 - 1:37 AM
If you want you can create a brand new java keystore. Refer to the following URL to know how to do it: http://nl.globalsign.com/en/support/ssl+certificates/java/java+based+webserver/keytool+commands/ .Otherwise you can use the keystore which comes with JDK. But I recommend to use new keystore which will have certs related to your IdP and SPs. Hope that explains.
#13 by FauxPas on July 20, 2013 - 4:56 AM
Also, can you please tell me why you haven’t included these 2 steps while creating the Signature:
3. Add the signature method algorithm URI with the method Signature#setSignatureAlgorithm(String). Note that the algorithm URI is dependent on the type of key contained with the signing credential.
4. Add the canonicalization method algorithm URI with the method Signature#setCanonicalizationAlgorithm(String). Note that unless there is good reason to do otherwise, and the ramifications are understood, the recommended canonicalization method for SAML signature use cases is exclusive canonicalization (with or without comments).
#14 by Jason513 on July 24, 2013 - 1:35 AM
These things will be automatically taken care by OpenSAML APIs. We don’t have to worry about it. That is the advantage of using it.
#15 by Francesca Karpf on February 17, 2014 - 1:41 PM
Usually I do not learn article on blogs, however
I would like to say that this write-up very compelled me to
try and do it! Your writing taste has been amazed me.
Thank you, quite nice post.
#16 by swebercelera on February 22, 2014 - 2:00 AM
I’m hoping you can answer this question. I’ve got the above coded and using a debugger and stepping through, I can see that the Response is getting a signature. But the last few lines (187 on) where we want to print out the XML is causing grief. When I marshall the Response the second time, the Signature is changed and lost.
How can I marshall out the Response without losing the Signature?
#17 by Anvesha on July 15, 2015 - 11:32 PM
Hi Narendra,
Thanks for posting this on your blog. It is really helpful. However, I am confused with the certificates. I have 2 certificates. One is the SP certificate which has a private key with password and the other one is the IDP certificate which has a trustedCertEntry with no password (public certificate of the IDP). In the code that you’ve posted above, I see that you are using the private key of the SP to sign the assertion. But, why have you called the jks file “idpcert.jks”? And you haven’t used the public key anywhere. Should the public key be used while sending the request so that only the IDP can decode it with its private key (and not while signing) ?
Your insight on this will be highly appreciated.
Thanks again,
Anvesha
#18 by pranky on November 18, 2016 - 5:38 PM
very helpful information ,
Thanks a lot Narendra
#19 by rufergon on February 14, 2017 - 8:45 PM
Thank you. You saved my life!