Как лучше всего проверить подпись XML в python?

Я пытаюсь проверить подпись XML-сообщения с заданным открытым ключом в Python, который отлично проверяется в коде PHP с помощью openssl.

Вот PHP-код, который работает нормально.

$pubKey = openssl_pkey_get_public(file_get_contents("public_key.pem"));

$xmlDoc = new DOMDocument();
$xmlDoc->load("message.xml");

$signedInfo=$xmlDoc->getElementsByTagName("SignedInfo")->item(0)->C14N(true, true);
$signature = base64_decode($xmlDoc->documentElement->getElementsByTagName("SignatureValue")->item(0)->nodeValue);

$ok = openssl_verify($signedInfo, $signature, $pubKey, OPENSSL_ALGO_SHA1);

Я нашел разные библиотеки в Python для достижения этой цели, но ни одна из них не проходит проверку. Я перечислил библиотеки и проблемы, с которыми я столкнулся. Есть ли другие предпочтительные способы достижения этого?

<сильный>1. pyOpenSSL

Выдается следующее сообщение: [('подпрограммы rsa', 'INT_RSA_VERIFY', 'неправильная длина подписи')]

import OpenSSL.crypto as c
from StringIO import StringIO
import xml.etree.ElementTree as xml_et
from myapp import settings

namespace = "{http://www.w3.org/2000/09/xmldsig#}"

xml_bytes = open(settings.STATIC_ROOT + '/file/test.xml', 'rt').read()
response_xml = xml_et.fromstring(xml_bytes.encode('utf-8'))
signature_elem = response_xml.find(namespace + 'Signature')
signature_value = signature_elem.find(namespace + 'SignatureValue').text

signed_info_output = StringIO()
signed_info_tree = xml_et.ElementTree(signature_elem.find(namespace + 'SignedInfo'))
signed_info_tree.write_c14n(signed_info_output)
signed_info = signed_info_output.getvalue()

# load certificate
cert = c.load_certificate(c.FILETYPE_PEM, open(settings.STATIC_ROOT + '/file/public.cert', 'rt').read())

# verify signature
try:
    c.verify(cert, signature_value, signed_info, 'sha1')
    print 'success'
except Exception, e:
    print 'fail'

<сильный>2. М2Крипто

Пытался установить M2Crypto, но не удалось найти заголовочный файл openssl/err.h. Итак, я установил openssl 1.1.0e и скопировал каталоги lib и include в каталог C:/pkg, и он выдает другую ошибку, например: SWIG/_m2crypto_wrap.c(3754): ошибка C2065: «CRYPTO_NUM_LOCKS»: необъявленный идентификатор И найден предварительно скомпилированный Установщик M2Crypto msi, но во время выполнения выдает следующую ошибку: ImportError: Ошибка загрузки DLL: указанный модуль не найден.

Эта библиотека кажется устаревшей и недостаточно доступной документации.

<сильный>3. signxml

Пока это единственная библиотека, которая у меня частично работает. Проверка XML работает нормально, но выдает ошибку на знаке: ValueError: Не удалось десериализовать данные ключа.

from xml.etree import ElementTree
from signxml import XMLSigner, XMLVerifier
from myapp import settings

cert = open(settings.STATIC_ROOT + '/file/public.cert', 'rt').read()
key = open(settings.STATIC_ROOT + '/file/public.key', 'rt').read()

root = ElementTree.fromstring('<xml1>12</xml1>')
signed_root = XMLSigner().sign(root, key=key, cert=cert)
verified_data = XMLVerifier().verify(signed_root).signed_xml
print verified_data

person digz6666    schedule 18.04.2017    source источник
comment
Подписи содержат произвольные байты и поэтому должны быть преобразованы в читаемые символы. Вы выполняете декодирование base64 в PHP, но, по-видимому, ничего эквивалентного в Python. Взгляните на модуль base64 (или binascii).   -  person guidot    schedule 19.04.2017
comment
Часть подписи не содержит байтов, она имеет строку в кодировке base64. Например: g1 + 4I5jTQWMUezyATWqUkdE6el4ewrx4WuRsDhXzlEM2hvZR3UoXBPCICfpyJOdlmvu9c3KI V3Pps59vajMCimCQpxm3r6 + TOqTUNrWGrYeyZr1U5ayK0nNXQZ17c / + bbRwaL5r0i725R7bu 5vUW + ZPrlfOkbvMXkJ5YWg5yVSzeuux9ih3JpYcKCsZA90oEjYNqcH0LnzEy / BC04p8HMMY1 vrISXfHcy4ZmpLPhPMz5uf6rG + o4bIbhhWyDIiHVcaT1ejLCg1NKC / БДК + u2te9XhBSxXp6R r59Jhu2gIh / JOEjBzaTGrNdW7P4CRUFxpRTPeOreTLfPryXV + dReuA ==   -  person digz6666    schedule 20.04.2017


Ответы (1)


Ответ на вопрос в заголовке signxml.

Это библиотека, предназначенная для заявленной цели. PyOpenSSL и M2Crypto работают с объектами более низкого уровня, чем XML-подпись; проверка последнего будет включать в себя канонизацию XML, анализ соответствующих его частей и сравнение предоставленной подписи с дайджестом. Хотя это возможно, это не тривиально и дает много места для ошибок. Например, в вашем коде для PyOpenSSL вы не декодируете значение подписи с помощью base64.

С signxml ваш пример в основном правильный. Для проверки подписи вам не нужен закрытый ключ, поэтому полученная вами ошибка не имеет отношения к вопросу. Как правило, вы должны читать сертификат и ключ в двоичном, а не в текстовом режиме (open(filename, "rb")) - даже если файлы закодированы в PEM.

Ниже приведен рабочий пример:

from xml.etree import ElementTree
from signxml import XMLSigner, XMLVerifier

cert = open("cert.pem", "rb").read()
key = open("key.pem", "rb").read()

xml_obj = ElementTree.fromstring("<Example/>")
signed_xml_obj = XMLSigner().sign(xml_obj, key=key)
XMLVerifier().verify(signed_xml_obj, x509_cert=cert)

Если ваш XML-объект был сериализован и десериализован после подписания, этого простейшего кода может быть недостаточно; (сложные) детали описаны здесь.

person Konstantin Shemyak    schedule 17.06.2019