在Python中使用signxml生成和验证签名时出现问题(命名空间和签名)

问题描述 投票:0回答:1

首先对不起我的英语。我正在Python中使用signxml迈出第一步,从我看到的文章来看,它看起来很容易使用,但我无法根据需要正确签署文档。

我正在查看网络上的文档和一些文章,在使用 signxml 签署文档时遇到的问题中,我在 https://technotes.shemyak.com/posts/xml-signatures- 找到了一篇文章with-python-elementtree/。所以我尝试使用这种结构的数据。

ET.register_namespace("ds", "http://www.w3.org/2000/09/xmldsig#")

xml_con_semilla = ET.fromstring("<getToken><item><Semilla>000009574333</Semilla></item><ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" Id=\"placeholder\"></ds:Signature></getToken>")

在这里他们解释了签名和验证时的一些困难。使用这个网站上的方法,即使在步骤结束时序列化数据,我也得到了正确的签名和验证,但是当我将 xml 发送到我需要的服务时,它给我带来了问题。我想这一定是因为它不具有所要求的结构。

服务等待的结构如下

<?xml version="1.0"?>
<getToken>
    <item>
        <Semilla>000009574333</Semilla>
    </item>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <DigestValue>XX...</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>XX...</SignatureValue>
        <KeyInfo>
            <KeyValue>
                <RSAKeyValue>
                    <Modulus>XX...</Modulus>
                    <Exponent>AQAB</Exponent>
                </RSAKeyValue>
            </KeyValue>
            <X509Data>
                <X509Certificate>XX...</X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
</getToken>

所以我正在从 etree 生成的 XML 中进行一次干净的尝试,因为我认为上述解决方案不能满足我的需要。

import xml.etree.ElementTree as ET
import signxml

semilla = '000009574333'
xml_semilla = ET.Element('getToken')
item = ET.SubElement(xml_semilla, 'item')
ET.SubElement(item, 'Semilla').text = semilla

>>> <getToken><item><Semilla>000009574333</Semilla></item></getToken>

semilla_xml_firmada = FirmarXMLSemilla(xml_semilla)
def FirmarXMLSemilla(xml_semilla):
 
    pem_cert, pem_key = [open(f, "rb").read() for f in ("cert.pem", "key.pem")]

    signer = XMLSigner(method=signxml.methods.enveloped,
                       signature_algorithm='rsa-sha1',
                       c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
                       digest_algorithm="sha1")

    signed_root = signer.sign(xml_semilla, key=pem_key, cert=pem_cert, always_add_key_value=True)

    try:
        verified_data = XMLVerifier().verify(signed_root, x509_cert=pem_cert)
        print('Documento Firmado Correctamente')
    except Exception as e:
        logging.error('Error de Verificación en la Firma o Respuesta SAML: {}'.format(e))

到目前为止,在没有序列化的情况下,签名是正确的

VerifyResult(signed_data=b'<getToken><item><Semilla>000009574333</Semilla></item></getToken>', signed_xml=<Element getToken at 0x24cd69e65c0>, signature_xml=<Element {http://www.w3.org/2000/09/xmldsig#}Signature at 0x24cd69e6640>)
    data_serialized = ET.tostring(signed_root)
data_serialized =
<getToken xmlns:ns0="http://www.w3.org/2000/09/xmldsig#">
    <item>
        <Semilla>000009574333</Semilla>
    </item>
    <ns0:Signature>
        <ns0:SignedInfo>
            <ns0:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            <ns0:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <ns0:Reference URI="">
                <ns0:Transforms>
                    <ns0:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                </ns0:Transforms>
                <ns0:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <ns0:DigestValue>XX...</ns0:DigestValue>
            </ns0:Reference>
        </ns0:SignedInfo>
        <ns0:SignatureValue>XX...</ns0:SignatureValue>
        <ns0:KeyInfo>
            <ns0:KeyValue>
                <ns0:RSAKeyValue>
                    <ns0:Modulus>XX...</ns0:Modulus>
                    <ns0:Exponent>AQAB</ns0:Exponent>
                </ns0:RSAKeyValue>
            </ns0:KeyValue>
            <ns0:X509Data>
                <ns0:X509Certificate>XX...</ns0:X509Certificate>
            </ns0:X509Data>
        </ns0:KeyInfo>
    </ns0:Signature>
</getToken>
    # Sending the data...
    data_parsed = ET.fromstring(data_serialized)

    try:
        verified_data = XMLVerifier().verify(data_parsed, x509_cert=pem_cert)
        print('Documento Firmado Correctamente')
        logging.debug(verified_data)
    except Exception as e:
        logging.error('Error de Verificación en la Firma o Respuesta SAML: {}'.format(e))
ERROR:root:Error de Verificación en la Firma o Respuesta SAML: Signature verification failed: bad signature

您会注意到,转换为字符串时,命名空间会添加到数据中,这会破坏签名。此外,还添加了“ns0:”前缀。

我需要将 xmlns="http://www.w3.org/2000/09/xmldsig#" 添加到签名中,而不是 getToken

我还尝试了Python signxml XML签名包。如何为签名标签添加xml占位符?但这也给我带来了问题。

我使用的是signxml版本3.0.1,这样我就可以使用SHA-1。

谢谢!

python xml elementtree xml-signature
1个回答
0
投票

我有同样的问题,似乎问题是你保存xml的方式,如果你使用etree,它应该可以做到“with open('key.pem', 'rb') as key_file: private_key_data = key_file.read()

    private_key = serialization.load_pem_private_key(
        private_key_data,
        password=None,  # Add your passphrase if the key is encrypted
        backend=default_backend()
    )
    with open('cer.rec.pem', 'rb') as cert_file:
        certificate = cert_file.read()
    tree = ET.parse('path_to_xml.xml')
    root = tree.getroot()

    signer = XMLSigner(
        method=methods.enveloped,
        c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'
    )
    # signer.sign_alg = SignatureMethod('http://www.w3.org/2000/09/xmldsig#rsa-sha1')
    signer.namespaces = {None: namespaces.ds}

    # Pass the private key object and certificate bytes to the signer
    signed_xml = signer.sign(
        root,
        key=private_key,
        # cert=certificate,
        always_add_key_value=True,
    )
    # XMLVerifier().verify(signed_xml)
    
    final_signed_xml = etree.ElementTree(signed_xml)
    final_signed_xml.write('signed_xml.xml', encoding='UTF-8', xml_declaration=False)`

同样使用signer.namespaces = {None: namespaces.ds}你应该能够消除命名空间

© www.soinside.com 2019 - 2024. All rights reserved.