[英]Manual verification of XML Signature
我可以成功地进行手动引用验证(规范化每个引用的元素 - > 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>
Ater删除了标签之间的所有空格(因此将整个元素放在一行上),我获得了这个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));
令我困惑的是两个摘要有不同的大小,但当然我还需要从两个计算中获得完全相同的值。 有什么建议? 谢谢。
MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig=
是DER编码的ASN.1结构的Base64编码: SEQUENCE
首先包含一个AlgorithmIdentifier
(声明这是SHA-1,没有参数,因为SHA-1不接受),然后是一个OCTET STRING
包含实际的20字节值。 在十六进制中,值为: dccdb8570286d36c94bba8e5107faee91e0df088
。
此ASN.1结构是标准RSA签名机制的一部分。 您正在使用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>
在</ds:SignedInfo>
之后的那个)。 生成的文件长度必须为930字节,其SHA-1哈希值为预期的dccdb85...
值。
查看您的特定XML令牌,我可以告诉您一些事情。
您正在使用Canonicalization方法Exclusive XML Canonicalization Version 1.0 。 这是确保您生成正确的摘要值和签名的一个非常重要的因素。
您正在使用相同的Canonicalization方法来计算引用摘要,以及在生成签名之前规范化SignedInfo 。
Exclusive XML Canonicalizaiton Version 1.0的规范由W3C制作,可以在各自的W3C建议书中找到。 如果您手动计算您的值,请确保您完全符合规范,因为规范化很难做到正确,并且正确执行此操作非常重要,否则您的值将不正确。
我刚刚写了一篇描述XML签名验证过程的文章。 这篇文章位于我的博客上 。 它比我的回答更详细地描述了这个过程,因为XML Signature有许多复杂性。 它还包含流行规范和RFC的链接。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.