Как подписать XML-документ с помощью Python?

Я делаю проект на Django (полный Python), а именно систему продаж. Сейчас я хочу включить в систему электронное выставление счетов, сосредоточившись пока только на электронном товарном чеке. В моей стране регулирующий орган требует, чтобы для отправки счета-фактуры он был оформлен в XML-файл (с определенным форматом), затем он должен быть подписан цифровым сертификатом компании, упакован в .zip-файл и отправлен на тестовый веб-сервис организации. Но я не знаю, как сделать подпись, используя только Python.

Я изучал вопрос и заметил, что большинство из них предпочитают использовать такие языки, как Java или C#. Я также нашел библиотеки lxml и cryptography, с помощью которых я могу сделать подпись, но когда я отправляю ее в веб-сервис, я получаю ошибку:

Error of the web service: The entered electronic document has been altered - Detail: Incorrect reference digest value

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

Я делюсь с вами кодом, чтобы дать вам представление о том, что я использовал:

А это тестовый XML-документ:

Вам нужно будет вставить узел подписи шаблона в xml-документ. Шаблон содержит подробную информацию о том, как подписать этот документ.

Затем вы просто указываете xmlsec подписать его. Он выведет правильный xml.

Вот документация по xmlsec

реализация на python из pip install xmlsec
примеров на python

Вот как вы заполняете подпись:

    from lxml import etree
import xmlsec

def sign_xml(xml_path, private_key_path, signed_xml_path):
    with open(xml_path) as fp:
        template = etree.parse(fp).getroot()
    
    signature_node = xmlsec.tree.find_node(template, xmlsec.constants.NodeSignature)
    assert signature_node
    
    ctx = xmlsec.SignatureContext()
    key = xmlsec.Key.from_file(private_key_path, xmlsec.constants.KeyDataFormatPem)
    ctx.key = key
    ctx.sign(signature_node)
    
    with open(signed_xml_path, 'wb') as out:
        out.write(etree.tostring(template))

Я предоставляю вам самим настроить шаблон подписи и добавить его в свой xml-документ, но я просто скопировал его из их документации (см. выше, раздел 2.1)

Вы можете использовать rsa, pip install rsa и позаботиться о пространстве имен:

Если вам нужно включить метаданные (например, название организации), вам придется создать сертификат X.509 вручную или использовать внешний инструмент, такой как OpenSSL, для создания сертификата с этой информацией.

Вернуться на верх