Ручная проверка подписи XML
-
08-10-2019 - |
Вопрос
Я могу успешно сделать руководственную справочную проверку (Canonicalize каждый элемент ссылки -> SHA1 -> Base64 -> Проверьте, если это то же самое содержимое дизначейства), но я терпит неудачу с проверкой подписивера. Вот вывеска для канонизации и хеш:
<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 Удаление всех пробелов между тегами (и поэтому получая весь элемент на одну строку), я получаю эту Diagest Sha1 (в Base64):
6L26IBH7IL / YRCQW6EEFV / VQAVO =
Теперь я ожидаю найти тот же дайджест после дешифрования содержания подписивале, но я получаю отличную и дольше стоимость:
Mcewcqyfkw4dahofaaqu3m24vwkg02yuu6jleh + U6R4n8ig =
Вот какой-то код 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));
Дело в том, что у меня смущает, что два дайджета имеют разные размеры, но, конечно, мне также нужно получить точно такое же значение из двух расчетов. Какие-либо предложения? Спасибо.
Решение
MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig=
Является ли BASE64 кодировкой для DER-кодированной структуры ASN.1: SEQUENCE
содержащий первый ан AlgorithmIdentifier
(который заявляет, что это SHA-1, без параметров, поскольку SHA-1 не принимает), то OCTET STRING
который содержит фактическое 20 байтовое значение. В шестнадцатеричном значении значение: dccdb8570286d36c94bba8e5107faee91e0df088
.
Эта структура ASN.1 является частью стандарта Подпись RSA механизм. Вы используете RSA дешифрование Для доступа к этой структуре, нестандартной. Вам на самом деле повезло что-нибудь вообще, так как Rsa шифрование а также Подпись RSA два отдельных алгоритма. Это так бывает, что они оба кормят в одном и том же клавишевых парах, и что «старый стиль» (AKA «PKCS # 1 V1.5»), и схемы шифрования и шифрования используют аналогичные методы заполнения (аналогичные, но не одинаковые; Уже немного удивительно, что реализация Java RSA не задыхается от подписи при подписи при использовании в режиме дешифрования).
В любом случае, 6l26iBH7il/yrCQW6eEfv/VqAVo=
Кодировка Base64 для 20-байтового значения, которое, в шестнадцатеричном, является: ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a
. Отказ Это то, что вы получаете, используя структуру XML, вы показываете выше, после удаления всех пробелов между тегами. Удаление всех пробелов нет правильная канонизация. На самом деле, насколько я знаю, пробел влияет только между атрибутами в рамках тегов, но внешнее пробеловое пространство должно быть не изменено (за исключением нормализации линии [LF / CR + LF]).
Значение, которое использовалось для генерации подписи ( dccdb85...
) можно получить с помощью объекта XML, который вы показываете, и удалив ведущие пробелы. Чтобы быть понятным: вы копируете + вставьте XML в файл, затем удалите ведущие пробелы (от 0 до 3 пробелов) на каждой строке. Вы убедитесь, что все в конце концов используют один LF (байт 0x0a), и вы удаляете окончательный LF (то, что сразу после </ds:SignedInfo>
). Полученный файл должен иметь длину 930 байтов, а его хеш SHA-1 ожидается dccdb85...
стоимость.
Другие советы
Глядя на ваш конкретный токен XML, я могу сказать вам несколько вещей.
Вы используете метод канонизации Эксклюзивная XML Canonicalization версии 1.0. Отказ Это очень ВАЖНЫЙ Фактор обеспечения того, чтобы вы создали правильные значения дайджеста и подпись.
Вы используете тот же метод канонизации как для вычисления ссылочных данных, так и для канонизации SignedInfo.перед произведением подписи.
Спецификация для эксклюзивной XML Canonicalizaiton версии 1.0 производится на W3C и может быть найдена в соответствующем Рекомендация W3C. Отказ Если вы вычисляете свои значения вручную, будьте уверены, что вы точно соответствуете спецификации, поскольку Canonicalization - это трудное, что нужно правильно, и очень важно сделать это правильно, в противном случае ваши значения будут неверными.
Я только что написал обширную статью, описывающую процесс проверки подписи XML. Статья находится в мой блог. Отказ Он описывает процесс гораздо более подробно, чем мой ответ, так как есть много тонкостей для подписи XML. Он также содержит ссылки на распространенные спецификации и RFC.