使用 OpenSAML 创建和签署响应(在 Java 中)但无法验证签名

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

我可以使用 OpenSAML 创建 SAMLResponse,但作为完整性检查,我想验证签名。接收者希望对断言进行签名而不是对响应进行签名,这看起来很好:

<?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>

如果我在 之前验证签名 编组响应,它是有效的:

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

}

但是,如果我通过添加以下代码来编组响应,即使我在 编组之前 设置签名,它也会返回无效:

    ...

    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

最后我尝试解组 编组响应,然后读取并验证签名,但它仍然无效,即使我可以验证响应与之前相同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

这是怎么回事?我在这里遗漏了什么吗?

解决了这个问题。断言需要一个不同于响应 ID 的 ID 值。我只需要添加以下代码行就可以了。

assertion.setID("1111111112");