Wednesday, August 10, 2016

Verifying signatures with OpenSAML v3

Here is the happy news of the day. Verifying a signature in OpenSAML V3 is done almost identical to how it is done in V2, so the blog post on the process from OpenSAML V2 is still very much relevant and worth checking out.

The only difference between the two version is that the SignatureValidator is no longer instantiated. Instead the validate method of SignatureValidator is now static and takes both the credentials and the signature object.

Below is the code for verifying signatures in OpenSAML V3
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
profileValidator.validate(assertion.getSignature());
SignatureValidator.validate(assertion.getSignature(), cred);

15 comments:

  1. Hello Stefan,
    I'm having trouble with this part.
    I'm currently using OpenSAML V3, I purchased your book and try to follow step by step with the sample project as a guide.

    In the 4th step, I recive the response and try to validate it, in order to do it I need the IDP credentials.
    In the sample project you create a keypair and put it in a Credential object that you use later in SignatureValidator.validate(assertion.getSignature(), cred);
    In real life, I have a Certificate file "example.cer" and have to somehow create a Credential object to use it in the validation.
    I can´t find a way to do it, I tried puting the certificate in a .jks but when you use the KeyStoreCredentialResolver you need a passwordMap, it has none because truststores by definition are a keystore without private keys, so you get "java.lang.UnsupportedOperationException: trusted certificate entries are not password-protected".

    I also tried not using keystores:

    Credential getCredencialIDP() throws CertificateException{
    Credential credential = null;
    try {
    InputStream in = new FileInputStream("/example.cer");
    CertificateFactory factory = CertificateFactory.getInstance("X.509");
    Certificate cert = factory.generateCertificate(in);
    credential.setPublicKey(cert.getPublicKey());
    } catch (FileNotFoundException ex) {
    java.util.logging.Logger.getLogger(acs.class.getName()).log(Level.SEVERE, null, ex);
    }
    return credential;
    }

    But then you have a problem casting it, you can't cast a BasicX509Credential to a Credential.

    Thank you in advance,

    Francisco Perdomo

    ReplyDelete
    Replies
    1. Let me correct the code I sent you, it was actually:
      BasicX509Credential getCredencialIDP() throws CertificateException{
      BasicX509Credential credential = null;
      try {
      InputStream in = new FileInputStream("/example.cer");
      CertificateFactory factory = CertificateFactory.getInstance("X.509");
      Certificate cert = factory.generateCertificate(in);
      credential.setPublicKey(cert.getPublicKey());
      } catch (FileNotFoundException ex) {
      java.util.logging.Logger.getLogger(acs.class.getName()).log(Level.SEVERE, null, ex);
      }
      return credential;
      }


      I also have a third failed attempt using Metadata:

      private Credential getCredencialIDP(){
      try {
      FilesystemMetadataResolver idpMetadataResolver = new FilesystemMetadataResolver(new File("C:\\idpTest2.xml"));
      idpMetadataResolver.setRequireValidMetadata(true);
      idpMetadataResolver.setParserPool(XMLObjectProviderRegistrySupport.getParserPool());
      idpMetadataResolver.setId("https://test-eid.portal.gub.uy/v1.1/idp");
      idpMetadataResolver.initialize();
      //KeyInfoCredentialResolver keyResolver = DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver();

      MetadataCredentialResolver credentialResolver = new MetadataCredentialResolver();
      credentialResolver.setRoleDescriptorResolver(new BasicRoleDescriptorResolver(idpMetadataResolver));
      //credentialResolver.setKeyInfoCredentialResolver(keyResolver);
      credentialResolver.initialize();
      CriteriaSet criteriaSet = new CriteriaSet();
      criteriaSet.add(new EntityIdCriterion("https://test-eid.portal.gub.uy/idp"));
      criteriaSet.add(new EntityRoleCriterion(IDPSSODescriptor.DEFAULT_ELEMENT_NAME));
      Credential credential = credentialResolver.resolveSingle(criteriaSet);
      return credential;
      }catch (ComponentInitializationException ex) {
      java.util.logging.Logger.getLogger(acs.class.getName()).log(Level.SEVERE, null, ex);
      } catch (ResolverException ex) {
      java.util.logging.Logger.getLogger(acs.class.getName()).log(Level.SEVERE, null, ex);
      }
      return null;
      }

      And I found the following problems with it:
      - if you run the code as is, you get "A KeyInfoCredentialResolver instance is required"
      - if I uncomment the KeyInfoCredentialResolver part I get the following:
      net.shibboleth.utilities.java.support.component.UninitializedComponentException: Component 'bfdd2fdf-4dab-42ed-ae55-1fa932edf088' has not yet been initialized and cannot be used.
      at net.shibboleth.utilities.java.support.component.ComponentSupport.ifNotInitializedThrowUninitializedComponentException(ComponentSupport.java:106)
      at org.opensaml.saml.metadata.resolver.impl.BasicRoleDescriptorResolver.resolve(BasicRoleDescriptorResolver.java:111)
      at org.opensaml.saml.metadata.resolver.impl.BasicRoleDescriptorResolver.resolve(BasicRoleDescriptorResolver.java:56)
      at org.opensaml.saml.security.impl.MetadataCredentialResolver.getRoleDescriptors(MetadataCredentialResolver.java:445)
      at org.opensaml.saml.security.impl.MetadataCredentialResolver.resolveFromMetadata(MetadataCredentialResolver.java:290)
      at org.opensaml.saml.security.impl.MetadataCredentialResolver.resolveFromSource(MetadataCredentialResolver.java:214)
      at org.opensaml.security.credential.impl.AbstractCriteriaFilteringCredentialResolver.resolve(AbstractCriteriaFilteringCredentialResolver.java:62)
      at org.opensaml.security.credential.impl.AbstractCredentialResolver.resolveSingle(AbstractCredentialResolver.java:36)

      Most of the code for the Metadata approach was from this page:
      http://stackoverflow.com/questions/42348805/creating-credential-object-from-idp-metadata-with-opensaml-v3

      But I haven´t been able to fix it and make it work, the instantiation of the keyinfocredentialresolver was from some forum, I don't have much idea of how it really works.

      Delete
    2. Have a look at org.opensaml.security.credential.CredentialSupport. The method getSimpleCredential takes a X509Cert and gives you a BasicX509Credential back. Hope it helps

      Delete
    3. There are two problems with that:
      1) The getSimpleCredential also takes a PrivateKey that you shouldn't have, to validate a signature you only require the certificate of the IDP (I'm validating the response from the IDP).
      2) The problem is that when I do :
      SignatureValidator.validate(assertion.getSignature(),credential);
      credential is required to be of the class Credential, not a BasicX509Credential, and as far as I know you can´t cast from one to the other.

      This is the error you get:
      org.opensaml.xml.security.x509.BasicX509Credential cannot be cast to org.opensaml.security.credential.Credential

      So how could I get an object "Credential" from the .cer file or the Metadata? Or if there is no way, how could I cast from BasicX509Credential to Credential?

      Delete
    4. 1. Private key is optional
      2. the BasicX509Credential returned from Credential support is org.opensaml.security.x509.BasicX509Credential and not org.opensaml.xml.security.x509.BasicX509Credential. The class actually returned implements org.opensaml.security.credential.Credential and can be casted

      Delete
    5. That corrections where what I needed, the Uruguayan governmental electronic identification team thanks you for your quick response and good disposition!
      We will probably keep contacting you in the future if something comes up.

      Delete
    6. Happy to hear and please do =)

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hi Stefan, i integrated saml with my web application . My query is how to get the assertion from SAML after authentication.
    I have one more external application which is invoked internally from my first web application. I want to retrieve the SAML assertion.

    ReplyDelete
    Replies
    1. After the authentication you web app receives a Response XML object which contains the Assertion. If you are using OpenSAML, you parse it using the unmarshaller and use the getter methods to get the assertion. It should go something like this. getResponse().getAssertions()

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. When I am trying to configure Remote Service provider in openam, I am getting this error:
    ERROR: An error occurred while importing the SAML metadata
    com.sun.identity.saml2.meta.SAML2MetaException: Unable to verify signature under element "EntityDescriptor".
    at com.sun.identity.saml2.meta.SAML2MetaSecurityUtils.verifySignature(SAML2MetaSecurityUtils.java:348)
    at com.sun.identity.saml2.meta.SAML2MetaUtils.preProcessSAML2Document(SAML2MetaUtils.java:680)
    at com.sun.identity.saml2.meta.SAML2MetaUtils.importSAML2Document(SAML2MetaUtils.java:657)
    at com.sun.identity.workflow.ImportSAML2MetaData.importSAML2MetaData(ImportSAML2MetaData.java:118)

    The signing certificate etc are imported in openam keystore and are valid ones, not sure what is why openam is throwing error while configure Remote SP. ALl was fine with opensaml2, issue is with opensaml3.
    Any help would be appreciated.

    ReplyDelete
  7. Ok, have you checked if there is any difference between the metadata produced from v2 and v3?

    ReplyDelete
  8. If you .initialize() that inlined new BasicRoleDescriptorResolver(idpMetadataResolver) you'll get rid of that net.shibboleth.utilities.java.support.component.UninitializedComponentException: Component 'bfdd2fdf-4dab-42ed-ae55-1fa932edf088' has not yet been initialized and cannot be used.

    I am taking my first steps in OpenSAML v3 (from v2) and it seems to be that you need to initialize everything like there is no tomorrow :)

    ReplyDelete