Ручная проверка подписи 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>

Ater удаление всех пробелов между тегами (и, таким образом, получение всего элемента на одной строке), я получаю этот дайджест sha1 (в В base64):

6l26iBH7il/yrCQW6eEfv / VqAVo=

теперь я ожидаю найти тот же дайджест после расшифровки содержимого SignatureValue, но я получаю differente и больше значение:

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

меня очень смущает то, что два дайджеста имеют разный размер, но, конечно, мне также нужно получить точно же значение из двух подсчетов. Есть предложения? Спасибо.

2 ответов


MCEwCQYFKw4DAhoFAAQU3M24VwKG02yUu6jlEH+u6R4N8Ig= является кодировкой Base64 для ASN с кодировкой DER.1 структура: a SEQUENCE содержащий первый элемент AlgorithmIdentifier (который утверждает, что это SHA-1, без параметров, так как SHA-1 не принимает ни одного), затем OCTET STRING, который содержит 20-байтовое значение. В шестнадцатеричном формате значение равно:dccdb8570286d36c94bba8e5107faee91e0df088.

это ASN.1 структура является частью стандарта подпись RSA механизм. Вы используете RSA дешифрования получить доступ к этой структуре, которая является нестандартной. Вы на самом деле повезло получить что-нибудь вообще, так как шифрование RSA и подпись RSA два различных алгоритма. Так получилось, что они оба питаются одними и теми же парами ключей, и что "старый стиль" (он же "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 Канонизации Версия 1.0. Это очень важно фактор гарантируя, что вы создаете правильные значения дайджеста и подпись.

  • вы используете один и тот же метод канонизации как для вычисления справочных дайджестов, так и для канонизации SignedInfo прежде чем поставить подпись.

спецификация для эксклюзивной версии 1.0 XML Canonicalizaiton производится W3C и может быть найдена в соответствующем рекомендация W3C. Если вы вычисляете свои значения вручную, убедитесь, что вы точно соответствуете спецификации, потому что канонизация-это трудная вещь, чтобы получить право, и очень важно сделать это правильно, иначе ваши значения будут неправильный.

Я только что написал обширную статью, описывающую процесс проверки подписи XML. Статья находится по адресу мой блог. Он описывает процесс гораздо более подробно, чем мой ответ, так как есть много тонкостей для подписи XML. Он также содержит ссылки на распространенные спецификации и РЛК.