8

我可以成功地进行手动引用验证(规范化每个引用的元素 --> 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=

这是一些用于解密的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));

让我很困惑的是两个摘要的大小不同,但当然我还需要从两个计算中获得完全相同的值。有什么建议么?谢谢你。

4

2 回答 2

8

MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig=是 DER 编码的 ASN.1 结构的 Base64 编码:aSEQUENCE首先包含 an AlgorithmIdentifier(表示这是 SHA-1,没有参数,因为 SHA-1 不接受任何参数),然后是OCTET STRING包含实际 20 字节值的 an。在十六进制中,值为:dccdb8570286d36c94bba8e5107faee91e0df088

此 ASN.1 结构是标准RSA 签名机制的一部分。您正在使用 RSA解密来访问该结构,这是非标准的。你实际上很幸运能得到任何东西,因为RSA 加密RSA 签名是两种不同的算法。碰巧它们都使用相同类型的密钥对,并且“旧式”(又名“PKCS#1 v1.5”)签名和加密方案使用相似的填充技术(相似但不相同;它是在解密模式下使用时,RSA 的 Java 实现并没有阻塞签名填充,这已经有点令人惊讶了)。

无论如何,6l26iBH7il/yrCQW6eEfv/VqAVo=是 20 字节值的 Base64 编码,在十六进制中是:ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a. 这是在删除标签之间的所有空白之后,通过散列上面显示的 XML 结构得到的结果。删除所有空格不是正确的规范化。实际上,据我所知,空格仅在属性之间、标签内受到影响,但外部空格必须保持不变(行尾规范化 [LF / CR+LF 事物] 除外)。

dccdb85...可以通过使用您显示的 XML 对象并删除前导空格来获得用于生成签名的值 (the )。需要明确的是:您将 XML 复制并粘贴到文件中,然后删除每行的前导空格(0 到 3 个空格)。您确保所有行尾都使用单个 LF(0x0A 字节)并删除最后的 LF(紧跟在 之后的那个</ds:SignedInfo>)。结果文件的长度必须为 930 字节,并且其 SHA-1 哈希值是预期dccdb85...值。

于 2010-12-13T16:25:30.247 回答
0

查看您的特定 XML 令牌,我可以告诉您一些事情。

  • 您正在使用规范化方法Exclusive XML Canonicalization Version 1.0。这是确保您生成正确的摘要值和签名的一个非常重要的因素。

  • 您正在使用相同的规范化方法来计算参考摘要,并 在生成签名之前对SignedInfo进行规范化。

Exclusive XML Canonicalizaiton Version 1.0 的规范由 W3C 制定,可在其各自的W3C Recommendation中找到。如果您手动计算您的值,请确保您完全符合规范,因为规范化是一件很难正确的事情,并且正确执行此操作非常重要,否则您的值将不正确。

我刚刚写了一篇描述 XML 签名验证过程的详尽文章。这篇文章位于我的博客。它比我的回答更详细地描述了这个过程,因为 XML 签名有很多错综复杂的地方。它还包含指向流行规范和 RFC 的链接。

于 2012-07-07T02:45:08.717 回答