Ручная проверка подписи XML - PullRequest
8 голосов
/ 12 декабря 2010

Я могу успешно выполнить ручную проверку ссылок (канонизировать каждый ссылочный элемент -> 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>

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

6l26iBH7il / yrCQW6eEfv / VqAVo =

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

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 ]

8 голосов
/ 13 декабря 2010

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

Эта структура ASN.1 является частью стандартного механизма подписи RSA .Вы используете RSA decryption для доступа к этой структуре, которая является нестандартной.На самом деле вам повезло что-либо получить, поскольку RSA-шифрование и RSA-подпись - это два разных алгоритма.Случается, что они оба используют одинаковые пары ключей, и что схемы подписи и шифрования старого стиля (PKCS # 1 v1.5) используют аналогичные методы заполнения (аналогичные, но не идентичные;уже немного удивительно, что реализация RSA на Java не подавляет заполнение сигнатуры при использовании в режиме расшифровки).

В любом случае 6l26iBH7il/yrCQW6eEfv/VqAVo= - это кодировка Base64 для 20-байтового значения, которое в шестнадцатеричном форматеэто: ea5dba8811fb8a5ff2ac2416e9e11fbff56a015a.Это то, что вы получаете, хэшируя структуру XML, показанную выше, после удаления всех пробелов между тегами.Удаление всех пробелов не надлежащей канонизации.На самом деле, насколько я знаю, пробелы затрагиваются только между атрибутами внутри тегов, но внешние пробелы должны оставаться неизменными (за исключением нормализации окончания строки [вещь LF / CR + LF]).

TheЗначение, которое использовалось для генерации подписи (dccdb85...), может быть получено с помощью показанного вами объекта XML и путем удаления начальных пробелов.Чтобы было понятно: вы копируете + вставляете XML в файл, затем удаляете начальные пробелы (от 0 до 3 пробелов) в каждой строке.Вы убедитесь, что все конец строки используют один LF (0x0A байт), и вы удаляете последний LF (тот, что сразу после </ds:SignedInfo>).Полученный файл должен иметь длину 930 байт, а его хэш SHA-1 является ожидаемым значением dccdb85....

0 голосов
/ 07 июля 2012

Глядя на ваш конкретный XML-токен, я могу рассказать вам несколько вещей.

  • Вы используете метод канонизации Эксклюзивный XML Канонизация Версия 1.0 . Это очень ВАЖНО фактор в обеспечении того, что вы производите правильные значения дайджеста и подписи.

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

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

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

...