Spring Security SAML: Replacing SHA-1 with SHA-256 on Signature and Digest Algorithms

PROBLEM

By default, Spring Security SAML’s SAMLBootstrap uses SHA1withRSA for signature algorithm and SHA-1 for digest algorithm.

@Configuration
@EnableWebSecurity
public abstract class AppSAMLConfig extends WebSecurityConfigurerAdapter {
	...

    @Bean
    public static SAMLBootstrap SAMLBootstrap() {
        return new SAMLBootstrap();
    }

	...
}

For example, the above configuration will generate the following SAML request payload when using HTTP-POST binding:-

<!--?xml version="1.0" encoding="UTF-8"?-->
<saml2p:authnrequest assertionconsumerserviceurl="https://server/app/saml/SSO" destination="https://adfs-server/adfs/ls/" forceauthn="true" id="a3bj4e05i70f6946gi85299i51i02a" ispassive="false" issueinstant="2016-02-23T15:10:26.414Z" protocolbinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://server/app/saml/metadata</saml2:issuer>
    <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="#a3bj4e05i70f6946gi85299i51i02a">
                <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:transform></ds:transform></ds:transforms>
                <ds:digestmethod algorithm="http://www.w3.org/2000/09/xmldsig#sha1">
                <ds:digestvalue>u25hV7rk8hIpXYLJQs0aZjkueP0=</ds:digestvalue>
            </ds:digestmethod></ds:reference>
        </ds:signaturemethod></ds:canonicalizationmethod></ds:signedinfo>
        <ds:signaturevalue>YDR9ybi...</ds:signaturevalue>
        <ds:keyinfo>
            <ds:x509data>
                <ds:x509certificate>MIICxz...</ds:x509certificate>
            </ds:x509data>
        </ds:keyinfo>
    </ds:signature>
    <saml2p:requestedauthncontext comparison="exact">
        <saml2:authncontextclassref xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:authncontextclassref>
    </saml2p:requestedauthncontext>
</saml2p:authnrequest>

Unfortunately, SHA-1 is now deemed insecure due to “Freestart Collision” attack.

Further, most modern browsers have ceased to trust SHA-1 code signing certificates starting January 2016 and will eventually stop accepting these certificates by January 2017.

SOLUTION

To fix this, we could replace SHA-1 with stronger secure hash algorithm, such as SHA-256.

To do so, create a class that extends SAMLBootstrap that uses SHA256withRSA for signature algorithm and SHA-256 for digest algorithm.

public final class CustomSAMLBootstrap extends SAMLBootstrap {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        super.postProcessBeanFactory(beanFactory);
        BasicSecurityConfiguration config = (BasicSecurityConfiguration) Configuration.getGlobalSecurityConfiguration();
        config.registerSignatureAlgorithmURI("RSA", SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
        config.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
    }
}

Then, return CustomSAMLBootstrap instead of SAMLBootstrap

@Configuration
@EnableWebSecurity
public abstract class AppSAMLConfig extends WebSecurityConfigurerAdapter {
	...

    @Bean
    public static SAMLBootstrap SAMLBootstrap() {
        return new CustomSAMLBootstrap();
    }

	...
}

Now, the generated SAML request payload using HTTP-POST binding looks like this:-

<!--?xml version="1.0" encoding="UTF-8"?-->
<saml2p:authnrequest assertionconsumerserviceurl="https://server/app/saml/SSO" destination="https://adfs-server/adfs/ls/" forceauthn="true" id="a2e7f98agfaec7d253714fjdbcf8a83" ispassive="false" issueinstant="2016-02-23T15:18:43.452Z" protocolbinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://server/app/saml/metadata</saml2:issuer>
    <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/2001/04/xmldsig-more#rsa-sha256">
            <ds:reference uri="#a2e7f98agfaec7d253714fjdbcf8a83">
                <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:transform></ds:transform></ds:transforms>
                <ds:digestmethod algorithm="http://www.w3.org/2001/04/xmlenc#sha256">
                <ds:digestvalue>w4qHFsBxFGifzemEJCYcuGOt+oZJ9N2DQM+Q2aEqJFI=</ds:digestvalue>
            </ds:digestmethod></ds:reference>
        </ds:signaturemethod></ds:canonicalizationmethod></ds:signedinfo>
        <ds:signaturevalue>YDR9ybi...</ds:signaturevalue>
        <ds:keyinfo>
            <ds:x509data>
                <ds:x509certificate>MIICxz...</ds:x509certificate>
            </ds:x509data>
        </ds:keyinfo>
    </ds:signature>
    <saml2p:requestedauthncontext comparison="exact">
        <saml2:authncontextclassref xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:authncontextclassref>
    </saml2p:requestedauthncontext>
</saml2p:authnrequest>

Comments

    • Choon-Chern Lim says:

      Yes. Those attributes are in the Spring Security’s authentication object. For example:-

      Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
      SAMLCredential credential = (SAMLCredential) authentication.getCredentials();

Leave a Reply

Your email address will not be published. Required fields are marked *