XML 签名:如何计算摘要值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2245320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
XML Signature: How to calculate the digest value?
提问by user252816
I have an XML like this
我有一个这样的 XML
<?xml version="1.0" encoding="utf-8"?>
<foo>
<bar>
<value>A</value>
</bar>
<bar>
<value>B</value>
</bar>
<baz>
<value>C</value>
</baz><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>WqpRWHxXA0YgH+p3Sxy6hRo1XIk=</DigestValue></Reference></SignedInfo><SignatureValue>EoRk/GhR4UA4D+8AzGPPkeim1dZrlSy88eF73n/T9Lpeq9IxoGRHNUA8FEwuDNJuz3IugC0n2RHQQpQajiYvhlY3XG+z742pgsdMfFE4Pddk4gF1T8CVS1rsF7bjX+FKT/c8B2/C8FNgmfkxDlB/ochtbRvuAGPQGtgJ3h/wjSg=</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIB8zCCAVygAwIBAgIQgfzbrIjhLL9FobStI2ub3zANBgkqhkiG9w0BAQQFADATMREwDwYDVQQDEwhUZXN0ZUFjbjAeFw0wMDAxMDEwMDAwMDBaFw0zNjAxMDEwMDAwMDBaMBMxETAPBgNVBAMTCFRlc3RlQWNuMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDO+yAZ8/qJbhSVH/+2wMmzix3jM/CExb6sTgaiPwe6ylcHgF45zeQDq06OSJZCSns34em/ULINZddDf8z0b9uk/2sOGr1pYqsunLLBvw2FkvWJQDkhx2SzCm8v4xGX2kyXNbjiY/K56oPOMjpayKoAFnnvk7p2iFAxNZK/6lpZ7wIDAQABo0gwRjBEBgNVHQEEPTA7gBCOOHcajwnATYZ0t6w7LVU0oRUwEzERMA8GA1UEAxMIVGVzdGVBY26CEIH826yI4Sy/RaG0rSNrm98wDQYJKoZIhvcNAQEEBQADgYEABL9Qhi6f1Z+/t8oKXBQFx3UUsNF9N2o4k6q1c3CKZYqx2E/in+nARIYRdh5kbeLfomi6GIyVFeXExp8crob3MAzOQMvXf9+ByuezimMPIHDvv0u3kmmeITXfoZrHCDxLoWWlESN1owBfKPqe7JKAuu9ORDC0pUiUfCHWxCoqNos=</X509Certificate></X509Data></KeyInfo></Signature>
</foo>
How the digest value (WqpRWHxXA0YgH+p3Sxy6hRo1XIk=) in the reference is created? I mean how can I compute this value manually?
参考中的摘要值 (WqpRWHxXA0YgH+p3Sxy6hRo1XIk=) 是如何创建的?我的意思是如何手动计算这个值?
回答by Kenny
I came across this question when attempting to find out the exact same thing. I later worked out how to do it, so figured I'd post the answer here.
我在试图找出完全相同的东西时遇到了这个问题。后来我想出了怎么做,所以想我会在这里发布答案。
The things that need to happen are:
需要发生的事情是:
- canonicalization
- create digest value, typically SHA1 (but could be SHA256 amongst others)
- base64 encode it
- 规范化
- 创建摘要值,通常是 SHA1(但也可能是 SHA256)
- base64 编码
The canonicalization part was fairly simple, as the Java libraries did that for me. What I struggled with was the next bit, the creating of the digest, because I made a fatal error in that the SHA1 digest I generated was the SHA1 in HEX form. SHA1 is 160 bits, so 20 bytes, but if you output these 160 bits in HEX, you get 40 characters. If you then base64 encode that, you get totally the wrong valuecompared to what should be in the DigestValue.
规范化部分相当简单,因为 Java 库为我做了这件事。我挣扎的是下一点,即摘要的创建,因为我犯了一个致命错误,因为我生成的 SHA1 摘要是十六进制形式的 SHA1。SHA1 是 160 位,所以是 20 字节,但是如果你以十六进制输出这 160 位,你会得到 40 个字符。如果然后对它进行 base64 编码,则与 DigestValue 中的值相比,您会得到完全错误的值。
Instead, you should generate the SHA1 digest and base64 encode the 20 byte output. Don't try to output the 20 bytes to STDOUT as it's highly unlikely to be readable (which is why people often output the HEX equivalent, since it isreadable). Instead, just base64 encode the 20 bytes and that's your DigestValue.
相反,您应该生成 SHA1 摘要并对 20 字节输出进行 base64 编码。不要尝试将 20 个字节输出到 STDOUT,因为它不太可能是可读的(这就是为什么人们经常输出等效的十六进制,因为它是可读的)。相反,只需对 20 个字节进行 base64 编码,这就是您的 DigestValue。
回答by user3273546
Is very simple, use openssl in the console:
很简单,在控制台中使用openssl:
openssl dgst -binary -sha1 file | openssl enc -base64
openssl dgst -binary -sha1 文件 | openssl enc -base64
Done
完毕
回答by KAD
This is a JAVA solution which requires the following jars:
这是一个 JAVA 解决方案,需要以下 jars:
- commons-logging-1.2.jar
- commons-codec-1.6.jar
- Saxon-HE-9.4.jar
- xmlsec-1.3.0.jar
- commons-logging-1.2.jar
- commons-codec-1.6.jar
- Saxon-HE-9.4.jar
- xmlsec-1.3.0.jar
This solution uses http://www.w3.org/2001/10/xml-exc-c14n#as the canonicalization algorithm, and uses SHA256as the hashing algorithm and base64 encoding.
该方案http://www.w3.org/2001/10/xml-exc-c14n#用作规范化算法,SHA256用作散列算法和base64编码。
Note:
documentrepresents the XML document as a DOM object in JAVA.
注意:
document在 JAVA 中将 XML 文档表示为 DOM 对象。
Code sample:
代码示例:
// create the transformer in order to transform the document from
// DOM Source as a JAVA document class, into a character stream (StreamResult) of
// type String writer, in order to be converted to a string later on
TransformerFactory tf = new net.sf.saxon.TransformerFactoryImpl();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
// create the string writer and transform the document to a character stream
StringWriter sw = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(sw));
String documentAsString = sw.toString();
// initialize the XML security object, which is necessary to run the apache canonicalization
com.sun.org.apache.xml.internal.security.Init.init();
// canonicalize the document to a byte array and convert it to string
Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
byte canonXmlBytes[] = canon.canonicalize(documentAsString.getBytes());
String canonXmlString = new String(canonXmlBytes);
// get instance of the message digest based on the SHA-256 hashing algorithm
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// call the digest method passing the byte stream on the text, this directly updates the message
// being digested and perform the hashing
byte[] hash = digest.digest(canonXmlString.getBytes(StandardCharsets.UTF_8));
// encode the endresult byte hash
byte[] encodedBytes = Base64.encodeBase64(hash);
return new String(encodedBytes);
回答by Max
I have encountered exactly this problem myself: I was generating an XML signature in Java & validating in .NET, and the validation always failed. In my case the cause was the 'print XML to file' function XMLWrite.m (yes, in MATLAB*) which was 'pretty printing' the XML, inserting tabs, spaces, and newlines as it saw fit. Since these are part of the document, naturally the validation failed (it failed in Java, too). Looking at your source, this may be happening to you. Use a Transformer (javax.xml.transform.*) to serialise your DOM properly without changing the content.
我自己也遇到过这个问题:我在 Java 中生成 XML 签名并在 .NET 中验证,但验证总是失败。在我的例子中,原因是“将 XML 打印到文件”函数 XMLWrite.m(是的,在 MATLAB* 中)它“漂亮地打印”了 XML,插入了它认为合适的制表符、空格和换行符。由于这些是文档的一部分,自然验证失败(在 Java 中也失败)。查看您的消息来源,这可能发生在您身上。使用 Transformer (javax.xml.transform.*) 在不更改内容的情况下正确序列化 DOM。
*You did know that MATLAB understands Java as well? You can just type Java statements into the interpreter console & they will be executed like native m-code.
*您知道 MATLAB 也能理解 Java 吗?您只需在解释器控制台中输入 Java 语句,它们就会像本机 m 代码一样执行。

