使用 Java XML 数字签名 API 的 xml 签名的摘要值错误



我需要将签名的XML文件发送到巴西的政府机构。问题是我的 Java 代码(使用 Java XML 数字签名 API(计算的摘要与使用其他工具(如 XMLSEC(生成的摘要不同。

以下是我用来为某个 XML 节点生成 XML 签名的代码:

private synchronized void sign(XmlObject obj) throws Exception {
        initKeystore();
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
        List<Transform> transformList = new ArrayList<Transform>();
        Transform envelopedTransform = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
        Transform c14NTransform = fac.newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
                (TransformParameterSpec) null);
        transformList.add(envelopedTransform);
        transformList.add(c14NTransform);
        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));
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")),
                System.getProperty("javax.net.ssl.keyStorePassword").toCharArray());
        KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("entry",
                new KeyStore.PasswordProtection(System.getProperty("javax.net.ssl.keyStorePassword").toCharArray()));
        X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
        // Create the KeyInfo containing the X509Data.
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        X509Data xd = kif.newX509Data(Collections.singletonList(cert));
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
        // Instantiate the document to be signed.
        Element el = (Element) obj.getDomNode().getFirstChild();
        String id = el.getAttribute("Id");
        DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), el);
        // Create the XMLSignature, but don't sign it yet.
        XMLSignature signature = fac.newXMLSignature(si, ki);
        // Marshal, generate, and sign the enveloped signature.
        signature.sign(dsc);
    }

如果我尝试使用 xmlsec 验证生成的 XML,则会出现以下错误:

$ xmlsec1 --verify consulta.xml 
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=229:obj=sha1:subj=unknown:error=12:invalid data:data and digest do not match
FAIL

但是,如果我尝试使用 xmlsec(使用相同的私钥(对同一个文件(consult.xml(进行签名,该错误就会消失:

xmlsec1 --sign --output doc-signed.xml --privkey-pem cert.pem consulta.xml

consult.xml 和 doc-signed.xml(由 xmlsec 生成(之间的区别在于 SignatureValue 和 DigestValue 标记的内容:

consulta.xml:

<DigestValue>Ajn+tfX7JQc0HPNJ8KbTy7Q2f8I=</DigestValue>
...
<SignatureValue>Q1Ys0Rtj8yL2SA2NaQWQPtmNuHKK8q2anPiyLWlH7mOIjwOs0GEcD0WLUM/BZU0Q
T0kSbDTuJeTR2Ec9wu+hqXXbJ76FpX9/IyHrdyx2hLg0VhB5RRCdyBEuGlmnsFDf
XCyBotP+ZyEzolbTCN9TjCUnXNDWtFP1YapMxAIA0sth0lTpYgGJd8CSvFlHdFj+
ourf8ZGiDmSTkVkKnqDsj8O0ZLmbZfJpH2CBKicX+Ct7MUz2sqVli4XAHs6WXX+E
HJpbOKthS3WCcpG3Kw4K50yIYGTkTbWCYFxOVsMfiVy4W/Qz15Vxb8chD8LM58Ep
m/szmvnTAESxv/piDr7hyw==</SignatureValue>

文档签名.xml:

<DigestValue>w6xElXJrZw3G86OsNkWav+pcKJo=</DigestValue>
...
<SignatureValue>YmUsnlnAY9uLhlfVBLhB8K8ArxMOkOKZJoQ6zgz55ggU6vJCO9+HWJCKQJp6Rvn/w5PCAFY0KJRb
r6/WhHML0Z+Q6TSuIL8OTvJ3iPoROAK6uy07YAflKOUklqk4uxgfMkR+hWMCyfITJVCVZo/MXmPy
g7YwmztoSlGH+p6+ND5n2u47Y2k6SpIvw3CUxwAVQkD0Hsj3G58cbUbrFCoyPVGOe4zJ9c1HPsMW
KzBEFe3QETzPJ8I1B7EEVi5oDvzXE2rMTH4K7zvNGnXpBNGwnSjEOticlqKVP5wyUD7CPwgF1Wgy
Z0njvlaW3K8YmAY8fc70v/+wSO6Fu+0zj18Xeg==</SignatureValue>

我不会发布这两个文件的其余部分,因为它们是平等的,并且会使这篇文章更加冗长。

据我所知,接收此 XML 文件的 Web 应用程序是一个 .NET 应用程序,它计算的签名摘要与我的 Java 代码不同(与 xmlsec 非常相似(。有什么想法吗?

如果现在回答还为时不晚:

您可以在代码中创建 2 个转换(envelopedTransform 和 c14NTransform(,但不使用它们。

使用单个新的 Transform.ENVELOPED 创建引用。不应用 http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (C14N( 转换。

现在,我不确定XML安全标准在这种情况下的行为应该是什么。也许其他工具也会自动应用 C14N 转换。

我肯定知道,如果您不指定任何转换,JDK 将至少应用 C14N 转换。

基本上改变那个 fac.newReference(", ...(并将 transformList 而不是 Collections.singletonList(( 传递给它。

理想情况下,DigestValue 元素包含 Java XML 签名 API 中实际的 base64 编码摘要值。您能否验证一下从 XMLSec 创建的摘要值也是 base64 编码的。

相关内容

  • 没有找到相关文章

最新更新