Sign SAML Assertion using OpenSAML


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. #1 by Karri on May 23, 2012 - 12:38 PM

    Why are marshalling the response twice?

  2. #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

      Response resp = SAMLWriter.getSamlAssertion();

      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

  3. #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.

      
      //create SignatureValidator
      SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
      
      //get the signature to validate from the response object
      Signature signature = samlResponse.getSignature();
      
      //try to validate
      try 
      {
           signatureValidator.validate(signature);
      }
      catch (ValidationException ve) 
      {
           System.out.println("Signature is NOT valid.");
           System.out.println(ve.getMessage());
           return;
      }
      

      Make sure that you are performing validation aganist response not the assertion.

      Regards,
      Narendra

  4. #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.

  5. #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

  6. #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.

  7. #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.

  8. #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?

  9. #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

  10. #18 by pranky on November 18, 2016 - 5:38 PM

    very helpful information ,

    Thanks a lot Narendra

  11. #19 by rufergon on February 14, 2017 - 8:45 PM

    Thank you. You saved my life!

Leave a reply to Francesca Karpf Cancel reply