XML签名:如何计算摘要值?

27

我有一个像这样的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>

如何创建参考文献中的摘要值(WqpRWHxXA0YgH+p3Sxy6hRo1XIk=)?我的意思是如何手动计算此值?


你为什么想要手动计算呢?那将是一个繁琐且容易出错的过程。 - HerbN
我正在使用 .net 函数来验证签名。它返回了签名无效。因此,我想手动检查它或者是否有任何工具可以检查签名... - user252816
1
我知道你解决了“参考的摘要验证失败”的错误?是否可能得到一个详细的解释,你是如何实际解决的?我正在使用Metro和.Net WCF应用程序签名SOAP消息时遇到这个问题,Digest Value无法被识别。 - Oscar Jara
4个回答

28

我在尝试查找同样问题的确切答案时遇到了这个问题。之后,我想出了如何解决它,并决定在这里发布答案。

需要完成以下操作:

  • 规范化
  • 创建摘要值,通常使用SHA1(但也可以是其他算法,如SHA256)
  • 对其进行base64编码

规范化部分相当简单,因为Java库已经为我做好了。我苦恼的是接下来的部分——创建摘要,因为我犯了一个致命错误,即我生成的SHA1摘要是以十六进制形式表示的。SHA1的长度是160位,即20字节,但如果您将这160位以十六进制形式输出,则会得到40个字符。如果您然后对其进行base64编码,与DigestValue中应该包含的内容完全不同。

相反,您应该生成SHA1摘要并对20字节的输出进行base64编码。不要尝试将20字节输出到STDOUT,因为它极有可能无法读取(这就是为什么人们经常输出HEX等效形式,因为它是可读的)。相反,只需对20字节进行base64编码,那么它就是您的DigestValue。


我也曾经这样想,但我得到了不同的值。你是直接处理规范化字节还是处理字符串字节? - Filippo Mazza
6
什么的SHA1摘要? - Dustin Sun
@lonelyloner:在签名节点被移除后,规范化输入XML的SHA1值。在我的情况下,XML是由.NET创建的,因此在规范化和创建SHA1之前,我还必须删除标签之间的\r \n和空格。 - Harry

8

非常简单,只需在控制台中使用openssl:

openssl dgst -binary -sha1 文件名 | openssl enc -base64

完成


OP没有将“手动”指定为“以编程方式”。无论如何,这是一个很好的检查方式。 - Vbakke
你知道如何获取签名值吗? - dev4life
发现这非常有用。我也想知道如何生成签名值,就像@dev4life上面那样。 - InspiredBy

3
我曾经遇到过类似的问题:我在Java中生成XML签名,然后在.NET中验证时,验证总是失败。在我的情况下,原因是'print XML to file'函数XMLWrite.m(是的,在MATLAB中)会“漂亮地打印”XML,根据需要插入制表符、空格和换行符。由于这些是文档的一部分,自然而然地验证失败了(在Java中也失败了)。从您的源代码来看,这可能也发生在您身上。使用Transformer(javax.xml.transform.*)正确序列化您的DOM,而不更改内容。
*您知道MATLAB也理解Java吗?您可以在解释器控制台中键入Java语句,它们将像本机m-code一样执行。

2
这已经被 XML Signature 解决了,它依赖于 XML 规范化。XML 首先被规范化,语法差异(空格、命名空间等)将被正确处理。 - ewernli
1
正确:但规范化的是DOM,而不是它的序列化。额外的空格字符是在序列化(到文件)期间插入的,摘要计算后它们仍然存在,当验证代码创建其DOM,规范化并重新计算摘要时。在解析的字符数据节点中,空格是有意义的。 - Max

3
这是一个JAVA解决方案,需要以下jar包:
  • commons-logging-1.2.jar
  • commons-codec-1.6.jar
  • Saxon-HE-9.4.jar
  • xmlsec-1.3.0.jar
此解决方案使用http://www.w3.org/2001/10/xml-exc-c14n#作为规范化算法,并使用SHA256作为哈希算法和base64编码。

注意:document表示JAVA中的XML文档DOM对象。

代码示例:
        // 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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接