Andrew
Andrew

Reputation: 209

Using OpenSAML to create and sign response (in Java) but having trouble validating the signature

I am able to create a SAMLResponse using OpenSAML, but as a sanity check I wanted to validate the signature. The recipient wants the Assertion to be signed but not the Response, which looks just fine:

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response ID="1111111111" Version="2.0"
    xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:Assertion Version="2.0"
        xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
                    xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"
                    xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                <ds:Reference URI="" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                    <ds:Transforms xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                        <ds:Transform
                            Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"
                            xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
                            xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"
                        xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                    <ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">H1M/MSp4KUwKryB9c99XvE6PWaU=
                    </ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                npeqj35bPh06G6WcSBzfpaDEqVUzhEOgqARKB6NIowGcAoD6yHpmDYTJ0nXvS31cRFYxqWtCVIrX
                e7abo3mpwfLI7FjpEawP+8IEaMi++b0hN1Xri/bprMNjxB7kldDGJc2WkbdVRAAIknQetxytd9gB
                WGhP97zLyGPVK23orH/JF09bFgoqGPgd/14fuY5ksOJuFfuZD4rsCOxxicYrfMsJDnlUfz8n18Xw
                Jj6vX/0SDoDknMz9h4kh1TU4tus0FvIAPYLNB7QV7Iarpd+5Q9R8mj+NJEviFH/DZlqE/NYrxRNt
                iinOgOnz1x3dtnDr2O/BHIW55mlBFi0L6wJ7ow==
            </ds:SignatureValue>
        </ds:Signature>
    </saml2:Assertion>
</saml2p:Response>

If I validate the signature before marshalling the response, it's valid:

protected static void testSignature(Credential credential) throws Exception {
    DefaultBootstrap.bootstrap();
    SignatureBuilder builder = new SignatureBuilder();
    Signature signature = builder.buildObject(); 
    signature.setSigningCredential(credential);
    signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
    signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

    Assertion assertion = new AssertionBuilder().buildObject();
    assertion.setVersion(SAMLVersion.VERSION_20);   
    assertion.setSignature(signature);

    AssertionMarshaller aMarsh = new AssertionMarshaller();
    aMarsh.marshall(assertion);
    Signer.signObject(signature);

    Response response = new ResponseBuilder().buildObject();
    response.setVersion(SAMLVersion.VERSION_20);
    response.setID("1111111111");
    response.getAssertions().add(assertion);

    Signature sig = response.getAssertions().get(0).getSignature();

    SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();        
    profileValidator.validate(sig);      
    SignatureValidator sigValidator = new SignatureValidator(credential);               
    sigValidator.validate(sig); //valid

}

However if I marshall the response by adding the following code, it comes back invalid even if I set the signature before marshalling:

    ...

    Signature sig = response.getAssertions().get(0).getSignature();

    ResponseMarshaller rMarsh = new ResponseMarshaller();
    Element plain = rMarsh.marshall(response);


    SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();        
    profileValidator.validate(sig);      
    SignatureValidator sigValidator = new SignatureValidator(credential);               
    sigValidator.validate(sig); //invalid

Lastly I tried to unmarshall the marshalled response, then read and validate the signature, and it still comes back invalid even though I can verify the response is the same as before marshalling/unmarshalling.

    ...

    ResponseMarshaller rMarsh = new ResponseMarshaller();
    Element plain = rMarsh.marshall(response);
    String samlResponse1 = XMLHelper.nodeToString(plain);

    UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
    Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(plain);
    XMLObject responseXmlObj = unmarshaller.unmarshall(plain);
    Response newResponse = (Response) responseXmlObj;


    plain = rMarsh.marshall(newResponse);
    String samlResponse2 = XMLHelper.nodeToString(plain);
    System.out.println(StringUtils.equals(samlResponse1, samlResponse2));  //true

    Signature sig = newResponse.getAssertions().get(0).getSignature();


    SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();        
    profileValidator.validate(sig);      
    SignatureValidator sigValidator = new SignatureValidator(credential);               
    sigValidator.validate(sig);  //invalid

What's going on? Is there something I'm missing here?

Upvotes: 1

Views: 3233

Answers (1)

Andrew
Andrew

Reputation: 209

Solved this. The Assertion requires an ID value that is different from the Response ID. I just needed to add the following line of code and it worked.

assertion.setID("1111111112");

Upvotes: 1

Related Questions