手动验证XML签名

时间:2010-12-11 21:55:23

标签: java web-services soap cryptography 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>

删除标签之间的所有空格(以便将整个元素放在一行上),我获得了这个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));

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

2 个答案:

答案 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的链接。