Подпись XML: как рассчитать значение дайджеста?

У меня есть такой 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=) в ссылке? Я имею в виду, как я могу вычислить это значение вручную?


person user252816    schedule 11.02.2010    source источник
comment
Почему вы хотите вычислить его вручную? Это будет утомительный и подверженный ошибкам процесс.   -  person HerbN    schedule 11.02.2010
comment
Я использую функции .net для проверки подписи. Возвращает, что подпись недействительна. Поэтому я хочу проверить это вручную или если бы был какой-либо инструмент для проверки подписи...   -  person user252816    schedule 11.02.2010
comment
Насколько я понимаю, вы решили, что проверка дайджеста не удалась из-за ошибки ссылки? Можно ли получить подробное объяснение того, как вы на самом деле это сделали? У меня возникла проблема с подписанным мыльным сообщением с использованием Metro, а приложение .Net WCF не распознало значение дайджеста.   -  person Oscar Jara    schedule 13.04.2012


Ответы (4)


Я столкнулся с этим вопросом, пытаясь выяснить то же самое. Позже я понял, как это сделать, поэтому решил опубликовать ответ здесь.

Вещи, которые должны произойти:

  • канонизация
  • создать значение дайджеста, обычно SHA1 (но среди прочего может быть SHA256)
  • base64 закодировать его

Часть канонизации была довольно простой, так как библиотеки Java сделали это за меня. С чем я боролся, так это со следующим моментом, созданием дайджеста, потому что я сделал фатальную ошибку в том, что сгенерированный дайджест SHA1 был SHA1 в HEX-форме. SHA1 — это 160 бит, то есть 20 байт, но если вы выведете эти 160 бит в HEX, вы получите 40 символов. Если вы затем закодируете это в base64, вы получите совершенно неправильное значение по сравнению с тем, что должно быть в DigestValue.

Вместо этого вы должны сгенерировать дайджест SHA1 и base64 закодировать 20-байтовый вывод. Не пытайтесь вывести 20 байтов в STDOUT, так как маловероятно, что они будут читаемы (именно поэтому люди часто выводят шестнадцатеричный эквивалент, поскольку он читается). Вместо этого просто base64 кодирует 20 байтов, и это ваше DigestValue.

person Kenny    schedule 28.09.2011
comment
Я думал так же, но я также получаю другое значение. Вы переваривали непосредственно байты канонизации или переваривали строковые байты? - person Filippo Mazza; 04.09.2012
comment
Дайджест SHA1 чего? - person Dustin Sun; 21.12.2017
comment
@lonelyloner: SHA1 канонизированного входного xml после удаления узла подписи. В моем случае xml был создан .NET, поэтому мне также пришлось удалить \r\n и пробелы между тегами перед канонизацией и созданием SHA1. - person Harry; 26.04.2019

Очень просто, используйте openssl в консоли:

файл openssl dgst -binary -sha1 | openssl enc -base64

Сделанный

person user3273546    schedule 05.02.2014
comment
ОП не указал «вручную» как «программно». В любом случае это хороший способ проверки. - person Vbakke; 24.05.2017
comment
Не могли бы вы также узнать, как получить значение подписи? - person dev4life; 23.08.2019
comment
Нашел это очень полезным. Также интересно генерировать signturevalue , как @dev4life выше. - person InspiredBy; 05.12.2019

Я сам столкнулся именно с этой проблемой: я генерировал XML-подпись на Java и проверял ее в .NET, и проверка всегда терпела неудачу. В моем случае причиной была функция «печати XML в файл» XMLWrite.m (да, в MATLAB*), которая «красиво печатала» XML, вставляя табуляции, пробелы и новые строки по своему усмотрению. Поскольку они являются частью документа, естественно, проверка не удалась (в Java она тоже не удалась). Глядя на ваш источник, это может происходить с вами. Используйте Transformer (javax.xml.transform.*) для правильной сериализации DOM без изменения содержимого.

*Вы знали, что MATLAB также понимает Java? Вы можете просто ввести операторы Java в консоль интерпретатора, и они будут выполняться как собственный m-код.

person Max    schedule 12.02.2010
comment
Это уже решается с помощью подписи XML, которая опирается на канонизацию XML. Сначала нормализуется XML, и синтаксические различия (пробелы, пространства имен и т. д.) будут обрабатываться правильно. - person ewernli; 12.02.2010
comment
Верно: но именно DOM, а не его сериализация, была канонизирована. Дополнительные пробельные символы были вставлены во время сериализации (в файл) ПОСЛЕ того, как дайджест был вычислен, и они все еще были там, когда проверяющий код создал свою модель DOM, канонизировал ее и повторно вычислил дайджест. Пробелы важны в проанализированных символьных узлах данных. - person Max; 12.02.2010

Это решение JAVA, для которого требуются следующие банки:

  • Commons-logging-1.2.jar
  • общий кодек-1.6.jar
  • Саксон-HE-9.4.jar
  • xmlsec-1.3.0.jar

Это решение использует http://www.w3.org/2001/10/xml-exc-c14n# в качестве алгоритма канонизации и использует SHA256 в качестве алгоритма хэширования и кодирования base64.

Примечание: document представляет документ XML как объект DOM в JAVA.

Пример кода:

        // 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);
person KAD    schedule 05.05.2017