手动验证XML签名

Joh*_*nca 8 java soap cryptography web-services 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>
Run Code Online (Sandbox Code Playgroud)

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));
Run Code Online (Sandbox Code Playgroud)

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

Tho*_*nin 8

MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig=是DER编码的ASN.1结构的Base64编码:a 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=Base64编码是一个20字节的值,以十六进制表示,它是:ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a.这是通过在删除标记之间的所有空格后散列上面显示的XML结构而获得的.删除所有空格不是正确的规范化.实际上,据我所知,空格仅在标签内的属性之间受到影响,但外部空白必须保持不变(除了行结束标准化[LF/CR + LF之外]).

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