手动验证XML签名 [英] Manual verification of XML Signature

查看:219
本文介绍了手动验证XML签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以成功地做手动参考验证(规范化每个被引用的元素 - > 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();
文档doc = builder.parse(新文件(inputFilePath));
NodeList nl = doc.getElementsByTagName(ds:SignatureValue);
if(nl.getLength()== 0){
throw new Exception(Can not find SignatureValue element);
}
特征码=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结构: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 = 是20字节值的Base64编码,十六进制为: ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a 。这是你通过散列你上面显示的XML结构,删除标签之间的所有空格后得到的。删除所有空格是不是正式规范化。事实上,据我所知,空格只在属性之间,在标签内,但外部空白必须保持不变(除了行结束标准化[LF / CR + LF东西])。



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


I can successfully do manual reference validation (canonicalize every referenced element --> SHA1 --> Base64 --> check if it's the same of DigestValue content) but I fail with the verification of the SignatureValue. Here's the SignedInfo to canonicalize and hash:

<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 removing all the spaces between tags (and so getting the whole element on a single line), I obtain this sha1 digest (in Base64):

6l26iBH7il/yrCQW6eEfv/VqAVo=

Now I expect to find the same digest after the decryption of the SignatureValue content, but I get a differente and longer value:

MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig=

Here's some java code for the decyption:

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

The thing that confuses me much is that the two digests have different size, but of course I also need to obtain exactly the same value from the two calculations. Any suggestions? Thank you.

解决方案

MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig= is Base64 encoding for a DER-encoded ASN.1 structure: a SEQUENCE containing first an AlgorithmIdentifier (which states that this is SHA-1, with no parameters since SHA-1 accepts none), then an OCTET STRING which contains the actual 20-byte value. In hexadecimal, the value is: dccdb8570286d36c94bba8e5107faee91e0df088.

This ASN.1 structure is part of the standard RSA signature mechanism. You are using RSA decryption to access that structure, which is non-standard. You are actually lucky to get anything at all, since RSA encryption and RSA signature are two distinct algorithms. It so happens that they both feed on the same kind of key pairs, and that the "old-style" (aka "PKCS#1 v1.5") signature and encryption schemes use similar padding techniques (similar but not identical; it is already a bit surprising that the Java implementation of RSA did not choke on the signature padding when used in decryption mode).

Anyway, 6l26iBH7il/yrCQW6eEfv/VqAVo= is Base64 encoding for a 20-byte value, which, in hexadecimal, is: ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a. This is what you get by hashing the XML structure you show above, after having removed all whitespace between tags. Removing all whitespace is not proper canonicalization. Actually, as far as I know, whitespace is affected only between attributes, within the tags, but external whitespace must be kept unchanged (except for line ending normalization [the LF / CR+LF thing]).

The value which was used for the signature generation (the dccdb85...) can be obtained by using the XML object you show and by removing the leading spaces. To be clear: you copy+paste the XML into a file, then remove the leading spaces (0 to 3 spaces) on each line. You make sure that all end-of-lines use a single LF (0x0A byte) and you remove the final LF (the one just after </ds:SignedInfo>). The resulting file must have length 930 bytes, and its SHA-1 hash is the expected dccdb85... value.

这篇关于手动验证XML签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆