繁体   English   中英

手动验证XML签名

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM