使用 XML 签名正确地签署 XML

Correctly signing XML with XMLSignature

我一直在开发 SAML2 SP,目前正在尝试为我的请求添加签名。

我已经通过以下方式创建了自签名证书:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3650 -nodes

我使用以下代码生成 XML 签名:

protected X509Certificate getCertificate() throws IOException, CertificateException
{
    Path x509crtfile = dataDir.resolve("cert.pem");
    PEMParser pem = new PEMParser(Files.newBufferedReader(x509crtfile, Charset.forName("UTF8")));
    return new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)pem. readObject());
}

protected RSAPrivateKey getKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
    Path x509keyfile = dataDir.resolve("key.pem");
    PEMParser pem = new PEMParser(Files.newBufferedReader(x509keyfile, Charset.forName("UTF8")));
    JcaPEMKeyConverter kc = new JcaPEMKeyConverter().setProvider("BC");
    return (RSAPrivateKey)kc.getPrivateKey((PrivateKeyInfo)pem.readObject());
}

protected void signRequest(Document request) throws IOException, InvalidAlgorithmParameterException,      NoSuchAlgorithmException, CertificateException, InvalidKeySpecException, MarshalException, XMLSignatureException
{
    DOMConfiguration docConfig = request.getDomConfig();
    docConfig.setParameter("infoset", Boolean.TRUE);
    request.normalizeDocument();
    String id = request.getDocumentElement().getAttributeNS("urn:oasis:names:tc:SAML:2.0:protocol", "ID");
    XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
    Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null), Collections.       singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
            null, null);
    SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
                CanonicalizationMethod.INCLUSIVE,
                (C14NMethodParameterSpec) null),
            fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
    KeyInfoFactory kif = fac.getKeyInfoFactory();
    List x509Content = new ArrayList();
    x509Content.add(getCertificate());
    X509Data xd = kif.newX509Data(x509Content);
    KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
    DOMSignContext dsc = new DOMSignContext(getKey(), request.getDocumentElement());
    dsc.setIdAttributeNS(request.getDocumentElement(), "urn:oasis:names:tc:SAML:2.0:protocol", "ID");
    XMLSignature signature = fac.newXMLSignature(si, ki);
    signature.sign(dsc);
}

// 调用代码片段:

            signRequest(doc);

            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer t = tf.newTransformer();
            DOMSource source = new DOMSource(doc);
            StringWriter sw = new StringWriter();
            StreamResult result = new StreamResult(sw);
            t.transform(source, result);
            byte[] encodedBytes = Base64.encodeBase64(sw.toString().getBytes());

示例输出: 测试 IdP 和使用在线工具的签名验证均失败。

<?xml version="1.0" encoding="UTF-8" standalone="no"?><samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" samlp:AssertionConsumerServiceURL="http://inferno.dunlop-lello.uk:8080/plugins/saml-authentication/sp/acs" samlp:Destination="https://shibboleth.dunlop-lello.uk/idp/profile/SAML2/POST/SSO" samlp:ID="3368168774849506" samlp:IssueInstant="2015-11-05T08:57:51" samlp:ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" samlp:Version="2.0"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://inferno.dunlop-lello.uk:8080/plugins/saml-authentication/</saml:Issuer><samlp:NameIDPolicy/><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>sai05O1Ug2BbxFg25WHST29rF0E=</DigestValue></Reference></SignedInfo><SignatureValue>nj6CX1wtYZf20rXKiY7qD5vtiOUc8S7zNYMkXa1ZyhcZs62V92O8rjOi0u/JZaLq7w2Fvwh/T5GQ
+Wvkz+XCJoZgNqv2DmJOZcwZkhZ5acoZM90tFWQPLptJj7IqS7T/egNTC7nXD0L61Ifn2DMhzliC qfT77SHpn5zieSKy20GoEaHYW9ucoDyON62Amghm5x1r7IKz8DoOqI19Au0ahOheIXjus7NqgLgJ eVgSOpoTpmyhZXa0c06+z1aYg+f0yMr91typYoFS3/IxMs4N7VRVVzX+O3/DbgqhVcM90N5bPgKd B/vQ61SMbYNRj5NRmedaaxCDkLDMQv+6LHahxQ==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIID3TCCAsWgAwIBAgIJAM3u0P2WViTzMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYDVQQGEwJHQjEU MBIGA1UECAwLV2VzdCBTdXNzZXgxFzAVBgNVBAcMDkhheXdhcmRzIEhlYXRoMSQwIgYDVQQKDBtE dW5sb3AtTGVsbG8gQ29uc3VsdGluZyBMVEQxIDAeBgNVBAMMF2luZmVybm8uZHVubG9wLWxlbGxv LnVrMB4XDTE1MTEwNDE3MjEyOFoXDTI1MTEwMTE3MjEyOFowgYQxCzAJBgNVBAYTAkdCMRQwEgYD VQQIDAtXZXN0IFN1c3NleDEXMBUGA1UEBwwOSGF5d2FyZHMgSGVhdGgxJDAiBgNVBAoMG0R1bmxv cC1MZWxsbyBDb25zdWx0aW5nIExURDEgMB4GA1UEAwwXaW5mZXJuby5kdW5sb3AtbGVsbG8udWsw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9nCCGd3Ikin15EkE2iMFUZx5C29CqZJjI RVxwPXBN+0SpPZBjiM30zAr4yeIg2YiMx9VfQsxprGUX2vpDzkR6yzgg+6LSACsoTB628pqqq4XH t5z1tKgpeRhef1Y+HA5AHT5ODI5YH4CyPxMXVdrjSfvvOe3mqCSHdkD2R0uPko5ZYzuxV/sQM3ZO wZmDvdLAj1W/16Z2v+l5CEbftI4wIbvRU6hwU7/ylA+gdAQdiIxlRf3mgzG6GW7sh9OK6A8pquBw YVZ7zLtAoV0QMb4r04nBF/N3wyMAdM8YSb3sio/2IxBkE4Osx2J2dH2V6sjBBnzkTZTQCLSuiO82 h6hFAgMBAAGjUDBOMB0GA1UdDgQWBBSJ7UW4/0xrutfCA4IBUp5esoyrADAfBgNVHSMEGDAWgBSJ 7UW4/0xrutfCA4IBUp5esoyrADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAXiu0E Z/A+YaqKrmm193rPJYCQrhl71P1OCBQysrYJIyygUZu3tXONaO/JvloNYLToFRO24kKdqf9WC7oh l2knIhPqoIc5/zsjiTsR9qQzNPHuhTA0GRquFvinR0rqdPBMuSfU+UjzOzXqkOxqr+X8L/e1IT5B 9Y66lGjJQcgzf71+C9m29aG+L4zzAYJURVwuljDYqVic6XmB5dUKdZn+7sNsuXkZp4u6QyyL7yEH rBbFF1tbPb7VXFroknbsUTIM5/Y36RzHWobS9LfYnwye8YyNP098UMWR5UalUCaityW6H6nUduge Du1z0L8uosGzsQr46KcBl038wi53ilzd</X509Certificate></X509Data></KeyInfo></Signature></samlp:AuthnRequest>

任何关于where/why这是错误的指示非常感谢。

看来上面的代码可能工作正常;我的发现发布在这里,希望它们能帮助下一个人 运行 解决签署 SAML 的问题。

我现在使用的是 OpenSAML 而不是推出我自己的解决方案,但遇到了同样的问题,结果证明这是 IdP 上的误导性错误消息。

具体来说,IdP 上没有 SP 的元数据,Shibboleth IdP 使用元数据进行签名验证,而不是嵌入式证书。我尝试用于验证的在线工具失败了,因为它无法识别根 CA,但再次出现误导性错误消息。