5

We can set the signature algorithm as following:

signature.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

I'm trying to find a way to set the DigestMethod algorithm like that. Is it possible via OpenSAML APIs? Any input is much appreciated.

UPDATE: Adding a sample Signature for the clarity. What this question concerned about is the DigestMethod element in it.

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <ds:Reference URI="#_884D49DAD03AD60748547F8322C11AA0">
        <ds:Transforms>
          <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
          <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <ds:DigestValue>...</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>...</ds:SignatureValue>
    <ds:KeyInfo>
      <ds:KeyName>...</ds:KeyName>
    </ds:KeyInfo>
  </ds:Signature>

UPDATE: Vladimír's answer works. However, that solution seems to be thread unsafe? In my application we bootstrap opensaml only once and then used by different threads with different configurations - like different signature algorithms. Is there a way to do this in a thread-safe manner?

UPDATE: Shibboleth IdP uses opensaml, and according to Shibboleth IdP Wiki this currently is a global configuration. So, regardless of IdP or SP side, this limitation should be there if opensaml is used to process SAML messages. Following is an excerpt from that article:

Changing the IdP signature/digest algorithm and related settings is currently a global operation. The algorithm will be changed for all relying parties it interacts with. Do not make this change until you have verified that all your relying parties can handle responses using the new algorithm(s) you choose

UPDATE: Finally found a way to get this done. Have added it as an answer.

drox
  • 7,523
  • 4
  • 23
  • 34

4 Answers4

9

This could be done thread safely by modifying the Signature's content references after setting the signature [1].

e.g.

authnRequest.setSignature(signature);

((SAMLObjectContentReference)signature.getContentReferences().get(0))
           .setDigestAlgorithm(EncryptionConstants.ALGO_ID_DIGEST_SHA256);

[1] https://lists.internet2.edu/sympa/arc/mace-opensaml-users/2007-10/msg00003.html

drox
  • 7,523
  • 4
  • 23
  • 34
7

Try the following call somewhere during initialization of your application:

  BasicSecurityConfiguration config = (BasicSecurityConfiguration) Configuration.getGlobalSecurityConfiguration();
  config.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
Vladimír Schäfer
  • 15,375
  • 2
  • 51
  • 71
  • 1
    Thanks! tried this and it worked. However, this seems to be a global configuration, thus thread unsafe. Am I correct? In my application we bootstrap opensaml only once and then used by different threads with different configurations - like different signature algorithms. Is there a way to do this in a thread-safe way? Appreciate your help. – drox Oct 29 '14 at 15:16
  • 2
    BTW, SignatureConstants doesn't have an ALGO_ID_DIGEST_SHA256. It's in org.opensaml.xml.encryption.EncryptionConstants. – drox Oct 29 '14 at 16:46
0

Looking through the OpenSAML source, another approach would be to extend the DefaultBootstrap, and DefaultSecurityConfigurationBootstrap classes, so thta this configuration is available for all your SAML implementation.

The CustomSamlBootstrap class can then be used instead of the DefaultSamlBootstrap to initialize your system.

For example:

import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.xml.ConfigurationException;    
public class CustomSamlBootstrap extends DefaultBootstrap {
    public static synchronized void bootstrap() throws ConfigurationException {

        initializeXMLSecurity();

        initializeXMLTooling();

        initializeArtifactBuilderFactories();

        initializeGlobalSecurityConfiguration();

        initializeParserPool();

        initializeESAPI();

        initializeHttpClient();
    }

    protected static void initializeGlobalSecurityConfiguration() {
        Configuration.setGlobalSecurityConfiguration(YourCustomSecurityConfigurationBootstrap.buildDefaultConfig());
    }

}

And

import org.opensaml.xml.security.BasicSecurityConfiguration;
import org.opensaml.xml.security.DefaultSecurityConfigurationBootstrap;
import org.opensaml.xml.signature.SignatureConstants;


public class YourCustomSecurityConfigurationBootstrap extends DefaultSecurityConfigurationBootstrap {
    public static BasicSecurityConfiguration buildDefaultConfig() {
        BasicSecurityConfiguration config = new BasicSecurityConfiguration();

        populateSignatureParams(config);
        populateEncryptionParams(config);
        populateKeyInfoCredentialResolverParams(config);
        populateKeyInfoGeneratorManager(config);
        populateKeyParams(config);

        return config;
    }

    /**
     * Populate signature-related parameters.
     *
     * @param config the security configuration to populate
     */
    protected static void populateSignatureParams(BasicSecurityConfiguration config) {
        // Asymmetric key algorithms
        config.registerSignatureAlgorithmURI("RSA", SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
        config.registerSignatureAlgorithmURI("DSA", SignatureConstants.ALGO_ID_SIGNATURE_DSA);
        config.registerSignatureAlgorithmURI("EC", SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);

        // HMAC algorithms
        config.registerSignatureAlgorithmURI("AES", SignatureConstants.ALGO_ID_MAC_HMAC_SHA1);
        config.registerSignatureAlgorithmURI("DESede", SignatureConstants.ALGO_ID_MAC_HMAC_SHA1);

        // Other signature-related params
        config.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
        config.setSignatureHMACOutputLength(null);
        config.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA1);
    }

}

Edit: Saw that you wanted to use different security configurations for different threads.

For this, you may want to extend the best suitable subclass of BaseSAML2MessageEncoder to override the signMessage function. Then pass YourCustomSecurityConfigurationBootstrap as needed to the SecurityHelper.prepareSignatureParams(Signature signature, Credential signingCredential, SecurityConfiguration config, String keyInfoGenName) method.

alex
  • 774
  • 5
  • 12
0

The way I did it was I created my own class TenantAwareSecurityConfiguration implementing SecurityConfiguration interface. It had one attribute delegate which eventually gets assigned to the default implementation of BasicSecurityConfiguration.

I did this in a separate bean which gets called after the initial instantiation has already happened. Inside this bean I initialize my own class and inside the constructor pass the default BasicSecurityConfiguration instance.

Now in my class I overridden the 2 methods for getting the digest and signing algorithm. For rest of the methods I just delegate calls to underlying delegate.

This way you can have your own implementation of these methods. In my case I needed to get tenant specific algorithms so these methods returned tenant specific algorithms from tenant properties.

Will post the code snippets soon.

Tulip Agarwal
  • 83
  • 1
  • 3