【问题标题】:Manual verification of XML Signature手动验证 XML 签名
【发布时间】:2010-12-11 21:55:23
【问题描述】:

我可以成功地进行手动引用验证(规范化每个引用的元素 --> 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 内容后找到相同的摘要,但我得到了一个不同的和 longer 值:

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));

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

【问题讨论】:

  • "删除标签之间的所有空格"...对吗?看着w3.org/TR/2001/…,听起来你可能删除了太多的空白。
  • 感谢您的回答。我理解你的观点,但是,正如我在问题开头所说的那样,我可以通过且仅通过删除这些空格来成功地对同一 SOAP 消息进行引用验证(两个引用,这不会是意外),所以我必须假设没错。
  • 规范化不仅仅是删除空格。它处理命名空间前缀问题、属性排序,以及通常会更改 phisicaly 文件的字节顺序(以及因此哈希摘要)的所有内容,但不会更改 XML 信息集(== XML 的含义负载)
  • 当然。我使用 org.apache.xml.security.c14n.Canonicalizer 但在我需要删除空格以获得与 xml 中相同的摘要之后。
  • 好吧,我找到了为什么解密的摘要这么大:它是由前缀+实际摘要(w3.org/TR/2002/REC-xmldsig-core-20020212/Overview.html)组成的。实际摘要在最后 20 个字节中。现在我有两个大小相同的摘要,但仍然彼此不同:(

标签: java web-services soap cryptography xml-signature


【解决方案1】:

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 事物] 除外)。

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

【讨论】:

  • 感谢您的明确和解决的答案!!! “删除前导空格”的想法来自哪里?无论如何,我想知道为什么 org.apache.xml.security.c14n.Canonicalizer 不删除它们以及为什么我需要两个不同的 xml 表示来完成这两个任务:对于参考验证,我必须删除标签之间的所有空格,而对于签名验证我必须做你写的。无论如何,这个 xml 消息是从 JBoss 部署的 servlet 发送并由 JBoss 部署的 Web 服务接收的。也许 JBoss 做了非标准的工作?
  • 我在一个试图解释规范化的网页上看到“删除前导空格”(我不记得是哪个)。规范化保持文本内容不变,包括空格(Apache canonicalizer 没有删除它们是正确的)。由于空格很麻烦,因此建议在最初构建 XML 时不使用任何缩进(即没有前导空格)。我的猜测是原始 XML 没有前导空格,它们是在某个时间点(签名计算之后)作为“阅读助手”添加的(这会弄乱签名)。
【解决方案2】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-18
    • 2019-06-14
    • 2019-11-21
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    相关资源
    最近更新 更多