[Python] SignXML Verifier Fails When XML Signature Uses Default Namespace Without ds: Prefix

Discussão em 'Python' iniciado por Stack, Julho 20, 2025.

  1. Stack

    Stack Membro Participativo

    I'm working on generating a signed XML document where the element (and related elements like , , etc.) must not include the ds: prefix. This is because the legacy application I’m integrating with expects the XML Digital Signature block to be present without any namespace prefixes—only the Signature tag and children in the correct namespace (i.e., http://www.w3.org/2000/09/xmldsig#), but without the ds: prefix.

    I’m using signxml to generate the signature. Signing works fine when I override the namespace as:

    signer.namespaces = {None: signxml.namespaces.ds}


    This gives me the desired XML structure (no ds:), and the output looks valid. However, when I try to verify the signature using signxml.XMLVerifier().verify(...), it fails. The same test passes if I don't override the namespace and let SignXML add the ds: prefix.

    Test Result


    self = <signxml.verifier.XMLVerifier object at 0xffffadce0c50>, element = <Element {http://www.w3.org/2000/09/xmldsig#}Reference at 0xffffacd2e9c0>, query = 'DigestMethod'
    require = True, xpath = ''

    def _find(self, element, query, require=True, xpath=""):
    namespace = "ds"
    if ":" in query:
    namespace, _, query = query.partition(":")
    result = element.find(f"{xpath}{namespace}:{query}", namespaces=namespaces)

    if require and result is None:
    > raise InvalidInput(f"Expected to find XML element {query} in {element.tag}")
    E signxml.exceptions.InvalidInput: Expected to find XML element DigestMethod in {http://www.w3.org/2000/09/xmldsig#}Reference

    /py/lib/python3.12/site-packages/signxml/processor.py:98: InvalidInput
    ================================================================================ short test summary info ================================================================================
    FAILED test_signxml.py::test_signed_without_ns_success - signxml.exceptions.InvalidInput: Expected to find XML element DigestMethod in {http://www.w3.org/2000/09/xmldsig#}Reference
    ============================================================================== 1 failed, 1 passed in 0.21s ==============================================================================


    Test Code


    """Test Certificate SetUp

    .. code-block:: bash

    openssl genrsa -out private.key 4096
    openssl req -new -key private.key -out request.csr
    openssl x509 -req -days 365 -in request.csr -signkey private.key -out certificate.crt


    """

    from xml.etree.ElementTree import Element, SubElement, tostring

    import lxml
    import signxml
    from cryptography import x509
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import serialization

    PRIVATE_KEY_PATH = "/app/private.key"
    CERTIFICATE_PATH = "/app/certificate.crt"


    def load_cert_pub(cert_path: str):
    with open(cert_path, "rb") as cert_file:
    cert_data = cert_file.read()
    cert = x509.load_pem_x509_certificate(cert_data, default_backend())
    return cert, cert.public_key()


    def load_private_key(key_path: str, password=None):
    with open(key_path, "rb") as key_file:
    key_data = key_file.read()
    key = serialization.load_pem_private_key(
    key_data, password=password, backend=default_backend()
    )
    return key


    def create_sample_xml():
    root = Element("ROOT")

    # Section Data
    data_sec = SubElement(root, "DATA")

    pid_elem = SubElement(data_sec, "ID")
    pid_elem.text = "encrypted_some_data_data"

    session_key_elem = SubElement(data_sec, "SESSION_KEY")
    session_key_elem.text = "encrypted_session_key"

    return tostring(root, encoding="unicode")


    private_key = load_private_key(PRIVATE_KEY_PATH)
    cert, public_key = load_cert_pub(CERTIFICATE_PATH)


    def test_signed_with_default_ns_success():
    xml_str = create_sample_xml()
    root = lxml.etree.fromstring(xml_str.encode("utf-8"))

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

    signed_root = signer.sign(
    root,
    key=private_key,
    exclude_c14n_transform_element=True,
    )

    signed_xml_str = lxml.etree.tostring(signed_root, encoding="utf-8")

    # Verifying
    root = lxml.etree.fromstring(signed_xml_str)

    result = signxml.XMLVerifier().verify(root, x509_cert=cert)

    # No exceptions, so this is okay...


    def test_signed_without_ns_success():
    # This test is failing...
    xml_str = create_sample_xml()
    root = lxml.etree.fromstring(xml_str.encode("utf-8"))

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

    signer.namespaces = {None: signxml.namespaces.ds}
    # signer.signature_annotators = [] # remove key info from payload

    signed_root = signer.sign(
    root,
    key=private_key,
    exclude_c14n_transform_element=True,
    )

    signed_xml_str = lxml.etree.tostring(signed_root, encoding="utf-8")

    # Verifying
    root = lxml.etree.fromstring(signed_xml_str)
    # This going to raise:
    # signxml.exceptions.InvalidInput: Expected to find XML element DigestMethod in {http://www.w3.org/2000/09/xmldsig#}Reference
    result = signxml.XMLVerifier().verify(root, x509_cert=cert)



    Side Note: I’m new to XML digital signatures, and trying to get this working based on the requirements of the external (legacy) application. If I’m missing something or doing in a wrong way, I'd appreciate your help on the correct approach.
    ​

    Continue reading...

Compartilhe esta Página