signxml: Incorrect signature in 2.10.0

A test in my project revealed that signxml 2.10.0 generates invalid signatures, while 2.9.0 generates correct signatures. The cause is the new excise_empty_xmlns_declarations parameter, which is appropriately set when validating a signature, but it also needs to be set when generating a signature.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16 (9 by maintainers)

Most upvoted comments

@kislyuk , thanks for looking into this. It takes some time for me to isolate and sanitize a test case.

This test only works with the branch of signxml I created at https://github.com/OpenPaymentNetwork/signxml . The company I’m working with accepts the signatures generated by our branch; it does not accept the signatures generated by signxml 2.10. That’s why I believe our branch is correct and signxml 2.10 is not. I am certainly open to the possibility that we’re calling signxml in some incorrect way.

The private key I included is used only for this test case and nothing else, so there’s no risk in sharing it.

import signxml
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from lxml.etree import Element, ElementTree, SubElement, fromstring, tostring
from OpenSSL.crypto import FILETYPE_PEM, load_certificate

signable = """\
<Message xmlns="urn:someco" xmlns:head="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">\
<AppHdr>\
<head:Fr>\
<head:FIId>\
<head:FinInstnId>\
<head:ClrSysMmbId>\
<head:MmbId>11234567890</head:MmbId>\
</head:ClrSysMmbId>\
</head:FinInstnId>\
</head:FIId>\
</head:Fr>\
<head:To>\
<head:FIId>\
<head:FinInstnId>\
<head:ClrSysMmbId>\
<head:MmbId>98765432100</head:MmbId>\
</head:ClrSysMmbId>\
</head:FinInstnId>\
</head:FIId>\
</head:To>\
<head:BizMsgIdr>B2022010201234567890BTST00000087654</head:BizMsgIdr>\
<head:MsgDefIdr>admn.005.001.01</head:MsgDefIdr>\
<head:CreDt>2022-01-02T03:04:05</head:CreDt>\
<head:Sgntr/>\
</AppHdr>\
</Message>\
"""

expected_output = """\
<Message xmlns="urn:someco" xmlns:head="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">
  <AppHdr>
    <head:Fr>
      <head:FIId>
        <head:FinInstnId>
          <head:ClrSysMmbId>
            <head:MmbId>11234567890</head:MmbId>
          </head:ClrSysMmbId>
        </head:FinInstnId>
      </head:FIId>
    </head:Fr>
    <head:To>
      <head:FIId>
        <head:FinInstnId>
          <head:ClrSysMmbId>
            <head:MmbId>98765432100</head:MmbId>
          </head:ClrSysMmbId>
        </head:FinInstnId>
      </head:FIId>
    </head:To>
    <head:BizMsgIdr>B2022010201234567890BTST00000087654</head:BizMsgIdr>
    <head:MsgDefIdr>admn.005.001.01</head:MsgDefIdr>
    <head:CreDt>2022-01-02T03:04:05</head:CreDt>
    <head:Sgntr>
      <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/2001/04/xmldsig-more#rsa-sha256"/>
          <Reference URI="">
            <Transforms>
              <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
              <Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <DigestValue>PP97CV3msqL3yEmD5fIVoJ9QOhVbacGUBGUq1kMENd4=</DigestValue>
          </Reference>
        </SignedInfo>
        <SignatureValue>VbEXHdNUv5KdJp8Wljzc2wxWM3OwqsfxWDA8L0NyZxfzh/Z7JLRzUiZ8p4ge1NB284aOUNBoSinEmLWoX2PEfbzptTgYm6Z3HGBFetLuhoIvwVzOywe4OkwEDi47ku4fWSL7D7tLwe8gGDyUQSf499I/VJnksPTE8EPrCphr+Ytv5D7jSurqobc9FhHBAhftcd40gEAruQzXsuem9b2mrr+oivqgIjGibNwbuc78xdIGbyuIQ1ybSRZj44tf1lzJ+r4/Je7q0oWoHeO2doeIMXkt59fY7n9CJLLbRGqkq2179OTwrktrvffiz8FOWOxwj/y18oDjuLL9tzPDBEsjEw==</SignatureValue>
        <KeyInfo>
          <X509Data>
            <X509SubjectName>CN=test.localdomain,C=US</X509SubjectName>
            <X509IssuerSerial>
              <X509IssuerName>CN=test.localdomain,C=US</X509IssuerName>
              <X509SerialNumber>508916181263576492682568652896711058344409241242</X509SerialNumber>
            </X509IssuerSerial>
          </X509Data>
        </KeyInfo>
      </Signature>
    </head:Sgntr>
  </AppHdr>
</Message>
"""


testcert = b"""\
-----BEGIN CERTIFICATE-----
MIIDMzCCAhugAwIBAgIUWSSXQEsgbnlB9LuLL8aAqyaXFpowDQYJKoZIhvcNAQEL
BQAwKDELMAkGA1UEBhMCVVMxGTAXBgNVBAMMEHRlc3QubG9jYWxkb21haW4wIBcN
MjIxMTE0MTYzMzExWhgPMjEyMjExMTQxNjMzMTFaMCgxCzAJBgNVBAYTAlVTMRkw
FwYDVQQDDBB0ZXN0LmxvY2FsZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxRJ0+uSUxVhrJSgFd/KNAO00lHlTt28gCxYPtZ1lh7Y34dA3RfEQ
KBeNbZftLwSjaekFyH7q2FsVwNTAuFuCVNSd8s2qLA7lg9YIAqYH72tom0hCvvjL
XdQmZoBs34pjFlEXIELpUJb+Zej/pFKXCJXO44rVKPmWSJ7QxcxcuC+60xnKr0Ep
RB/cFuYhEt6x8G5K1zgfO+IDkXCZilpzlUMfdXmSQFM01JAR9MXkAL+M4apYOQI2
q0fC6whLCaExUmek890uBHgUTTFUE6EqZlCSjm3JU4rjqRgyq7d8wIfl81POxjzn
+WFheP0LYv0tM0HHI6UGDMnsBw97QvhgwwIDAQABo1MwUTAdBgNVHQ4EFgQUAhHg
NYhCQ+sxMQLNwySM+1w3H5IwHwYDVR0jBBgwFoAUAhHgNYhCQ+sxMQLNwySM+1w3
H5IwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAR9vo+FRn4qpp
0GqH9tLAM3PdOo6twHkIHF+1TVmdsKbYN55x23JFOin0aJfp3wNgj23JQbkLGFGN
UHEL9QvLV0CdEAcHPJz6l9uJJYeOsW1XmSe6EwHHSXgkIj+btB/oL8wuPJJSTc8e
+3XiSvOdX4brvELwxIqNKwdpUvv8Bip0lMyc9BGTMfIBSBhuAYrw/vef17SZL3VW
6/MbU/CnMtwvVKFFVFZUt3AHLd7t2mW69QUfypxm51/HwRZtiM91GNplzFyKxbD3
QQ4qGZnb9uJwr6KVojHpQ7aVU2u/SQHlNQecEIFBPE1iJ/2eghGfdUHoLyXb/x9k
aSmD07X14Q==
-----END CERTIFICATE-----
"""


testkey = b"""\
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFEnT65JTFWGsl
KAV38o0A7TSUeVO3byALFg+1nWWHtjfh0DdF8RAoF41tl+0vBKNp6QXIfurYWxXA
1MC4W4JU1J3yzaosDuWD1ggCpgfva2ibSEK++Mtd1CZmgGzfimMWURcgQulQlv5l
6P+kUpcIlc7jitUo+ZZIntDFzFy4L7rTGcqvQSlEH9wW5iES3rHwbkrXOB874gOR
cJmKWnOVQx91eZJAUzTUkBH0xeQAv4zhqlg5AjarR8LrCEsJoTFSZ6Tz3S4EeBRN
MVQToSpmUJKObclTiuOpGDKrt3zAh+XzU87GPOf5YWF4/Qti/S0zQccjpQYMyewH
D3tC+GDDAgMBAAECggEAS1BwcGnnCTxvRjXzURHbHV2J2Kw9eD75ygLWYhwS7ziO
RrhiR1KHUCsgG6ASPwPQx+sMEMGgUqsMtzMXq90dA/p1mpNK06elwyqKyShWpAMy
aRXGnGFMp0Eqo8W01gF3ONIoumWX8PqjoqLXAB+oJNnLxV4LdKtd6cb4Fn+xd5nq
G4FDZxrdEb+Kzz36egCC9O9AJdxh2H+Z0hq8WBVFPedcwxK7OBSP2H/9LmPV+Ulp
+ol2xvtyIixOuAZ91dMi/XOJVQnsCjuqRfd+USB1OR58/0LR3zt37F5bRqddM71R
dwL8qrRoFlkuWJPBY68ACZdSI60XnYKfmPQ0jyNdIQKBgQDuQ44duRjtq8jWzH6o
jAXUoyHHBtoY41SUCJ/fbbP+N/3fyXKpHkH/hpL3iY/CMz0ddflleFps8rfm0gyM
10uIzFRxmjqFXdumsB/XVD5QDYpRSBGPgAoQOWM95UDdAZ+0y6rwfIFlj5mQabPF
bbneD+TILnMNYiGVv5n0hxk8OQKBgQDTve+Re7RdEs3TfCckOPjTmPQ7zfMdNkF9
LCotpNdiTM7mpBSAeu65AQuUZQcHcOlpOiUBtjo/Cy+Bc+DXmnAPwHP8vh4JuunR
YEukmpo1EwxkQRvCa/LnL3Qd1WuwVWtBm5A31C2CLj1FgIP78SEsVtLEa8oR6CVU
tZrMOWi82wKBgQDInECwbgS7OIRlttB+AUd3fdMWzIIdqmqwKYLSkH+YcHRlVCwi
kMNo7loX6c8M6C9w+r/925PnX4XP5tOCUlPa3o/LBtx3Jkum0Ww91USSCpSHU3LV
rCzM9ETG/JvceV2K0GEhEvOwG3Rkz1r5xCVW/3LVyaY9gn5co3JxHL72oQKBgBU/
3Xf7MymYBoHv1rnC4e3STshlrb1DwaS/Nuhnv5nE3Yq18rzGGYkFetUEooTzpukB
hc87K3NCOx1BtNHOVOqvxLQbnEYbtPVnNZAqV8l1xOuWwSNs0+6xi3SnA2yp1d66
rul7aKnE3C9Ka3RwSYT6naJKsvfDFWP/6a4PINy7AoGBAMv6NHe4wXs7koIZXtWp
SZ2km6TS0qTpHNesSRodn7gldqMgqJPbZCNNkW3HkHQdp4kszufpaFagrrEmV3Kk
IoKL4RO2+FBQgryGgYgRohDtuXASoct6ZsxMlQk7IgEtzmQnBQnU2JWo6xjf2Xtp
877zDFys+ozVSNmsMqOF5Ad/
-----END PRIVATE KEY-----
"""


def make_key_info_elem(cert):
    elem = Element("{http://www.w3.org/2000/09/xmldsig#}KeyInfo")
    data = SubElement(elem, "{http://www.w3.org/2000/09/xmldsig#}X509Data")
    subj = SubElement(data, "{http://www.w3.org/2000/09/xmldsig#}X509SubjectName")
    subj.text = "CN=test.localdomain,C=US"
    iserial = SubElement(data, "{http://www.w3.org/2000/09/xmldsig#}X509IssuerSerial")
    iname = SubElement(iserial, "{http://www.w3.org/2000/09/xmldsig#}X509IssuerName")
    iname.text = "CN=test.localdomain,C=US"
    inum = SubElement(iserial, "{http://www.w3.org/2000/09/xmldsig#}X509SerialNumber")
    inum.text = f"{cert.get_serial_number()}"
    return elem


def test_sig():
    nsmap_xmldsig = {None: "http://www.w3.org/2000/09/xmldsig#"}
    signable_tree = ElementTree(fromstring(signable))

    sig_envelope = signable_tree.find(
        "//{urn:iso:std:iso:20022:tech:xsd:head.001.001.01}Sgntr"
    )
    assert sig_envelope is not None

    SubElement(
        sig_envelope,
        "{http://www.w3.org/2000/09/xmldsig#}Signature",
        attrib={"Id": "placeholder"},
        nsmap=nsmap_xmldsig,
    )

    ssl_cert = load_certificate(FILETYPE_PEM, testcert)
    ssl_key = load_pem_private_key(testkey, password=None)

    key_info_elem = make_key_info_elem(ssl_cert)

    signer = signxml.XMLSigner(
        signature_algorithm="rsa-sha256",
        c14n_algorithm="http://www.w3.org/2006/12/xml-c14n11",
        signed_info_c14n_algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
        digest_algorithm="sha256",
    )
    signer.namespaces = nsmap_xmldsig
    signed_root = signer.sign(
        signable_tree,
        key=ssl_key,
        cert=ssl_cert,
        key_info=key_info_elem,
    )

    signed_output = tostring(signed_root, encoding="unicode")
    print(signed_output)
    signed_elem = fromstring(signed_output)
    pretty = tostring(signed_elem, pretty_print=True, encoding="unicode")
    print(pretty)

    assert pretty == expected_output, "mismatched"


if __name__ == "__main__":
    test_sig()