我可以成功地进行手动参考验证(规范化每个引用的元素 - > SHA1 - > Base64 - >检查它是否与DigestValue内容相同)但是我对SignatureValue的验证失败了。这是规范化和散列的SignedInfo:
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>
<ds:Reference URI="#element-1-1291739860070-11803898">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
<ds:DigestValue>d2cIarD4atw3HFADamfO9YTKkKs=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#timestamp">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
<ds:DigestValue>YR/fZlwJdw+KbyP24UYiyDv8/Dc=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
删除标签之间的所有空格(以便将整个元素放在一行上),我获得了这个sha1摘要(在Base64中):
6l26iBH7il / yrCQW6eEfv / VqAVo =
现在我希望在解密SignatureValue内容后找到相同的摘要,但我得到一个不同的更长的值:
MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH + u6R4N8Ig =
以下是decyption的一些java代码:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new File(inputFilePath));
NodeList nl = doc.getElementsByTagName("ds:SignatureValue");
if (nl.getLength() == 0) {
throw new Exception("Cannot find SignatureValue element");
}
String signature = "OZg96GMrGh0cEwbpHwv3KDhFtFcnzPxbwp9Xv0pgw8Mr9+NIjRlg/G1OyIZ3SdcOYqqzF4/TVLDi5VclwnjBAFl3SEdkyUbbjXVAGkSsxPQcC4un9UYcecESETlAgV8UrHV3zTrjAWQvDg/YBKveoH90FIhfAthslqeFu3h9U20=";
X509Certificate cert = X509Certificate.getInstance(new FileInputStream(<a file path>));
PublicKey pubkey = cert.getPublicKey();
Cipher cipher = Cipher.getInstance("RSA","SunJCE");
cipher.init(Cipher.DECRYPT_MODE, pubkey);
byte[] decodedSignature = Base64Coder.decode(signature);
cipher.update(decodedSignature);
byte[] sha1 = cipher.doFinal();
System.out.println(Base64Coder.encode(sha1));
令我困惑的是两个摘要的大小不同,但当然我还需要从两个计算中获得完全相同的值。有什么建议?谢谢。
答案 0 :(得分:8)
MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig=
是DER编码的ASN.1结构的Base64编码:SEQUENCE
首先包含AlgorithmIdentifier
(表明这是SHA-1,因为SHA没有参数-1接受无),然后是OCTET STRING
,其中包含实际的20字节值。在十六进制中,值为:dccdb8570286d36c94bba8e5107faee91e0df088
。
此ASN.1结构是标准RSA signature机制的一部分。您正在使用RSA 解密来访问该结构,这是非标准的。你真的很幸运能得到任何东西,因为 RSA加密和 RSA签名是两种截然不同的算法。碰巧它们都以相同类型的密钥对为基础,并且“旧式”(又名“PKCS#1 v1.5”)签名和加密方案使用类似的填充技术(类似但不相同;它是已经有点令人惊讶的是,RSA的Java实现在解密模式下使用时没有阻塞签名填充。)
无论如何,6l26iBH7il/yrCQW6eEfv/VqAVo=
是一个20字节值的Base64编码,以十六进制表示,它是:ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a
。这是通过在删除标记之间的所有空格后散列上面显示的XML结构而获得的。删除所有空格是不正确的规范化。实际上,据我所知,空格只受标签内属性之间的影响,但外部空格必须保持不变(除了行结束标准化[LF / CR + LF之外])。
可以使用您显示的XML对象并删除前导空格来获取用于签名生成的值(dccdb85...
)。要清楚:您将XML复制+粘贴到文件中,然后删除每行上的前导空格(0到3个空格)。确保所有行尾都使用单个LF(0x0A字节)并删除最终的LF(</ds:SignedInfo>
之后的那个)。生成的文件长度必须为930字节,其SHA-1哈希值为预期的dccdb85...
值。
答案 1 :(得分:0)
查看您的特定XML令牌,我可以告诉您一些事情。
您正在使用Canonicalization方法独占XML 规范化版本1.0 。这是一个非常重要因素 确保您生成正确的摘要值和签名。
您使用相同的Canonicalization方法来计算参考摘要,以及规范化 SignedInfo 在制作签名之前。
Exclusive XML Canonicalizaiton Version 1.0的规范由W3C制作,可以在各自的W3C Recommendation找到。如果您手动计算您的值,请确保您完全符合规范,因为规范化很难做到正确,并且正确执行此操作非常重要,否则您的值将不正确。
我刚刚写了一篇描述XML签名验证过程的文章。该文章位于my blog。它比我的回答更详细地描述了这个过程,因为XML Signature有许多复杂性。它还包含流行规范和RFC的链接。