xmlsign

Format XML

   1 sudo apt install libxml2-utils # install in debian
   2 xmllint --format payload.xml  # format XML
   3 

Example

main.py

   1 '''
   2 pip install signxml lxml
   3 # generate the x509 cert and private key
   4 openssl req -new -x509 -days 3650 -nodes -out cert.pem -keyout privkey.pem
   5 
   6 apt install libxml2-utils
   7 
   8 xmllint --schema ./test.xsd ./test_signed.xml 
   9 xmllint --schema ./test.xsd ./test.xml 
  10 
  11 '''
  12 
  13 from signxml import XMLVerifier, XMLSigner, methods, XMLSignatureProcessor
  14 from lxml import etree
  15 
  16 XML_SIG_NS = ns = {"ds":"http://www.w3.org/2000/09/xmldsig#"}
  17 X509_CERT = "//ds:X509Certificate"
  18 PUB_KEY = "cert.pem"
  19 PRIV_KEY = "privkey.pem"
  20 
  21 def get_x509_cert(xml_signed_root):
  22     assert(type(xml_signed_root) is str)
  23     extracted_x509_cert = etree.fromstring(xml_signed_root).xpath(X509_CERT, namespaces=XML_SIG_NS)[0].text
  24     return extracted_x509_cert
  25 
  26 def sign_payload1():
  27     print "\n payload1"
  28     payload = open("payload1.xml").read()
  29     cert = open(PUB_KEY).read()
  30     key = open(PRIV_KEY).read()
  31     root = etree.fromstring(payload)
  32     signed_root = XMLSigner().sign(root, key=key, cert=cert)
  33     xml_signed_root = etree.tostring(signed_root)
  34     open("payload1_signed.xml", "wb").write(xml_signed_root)
  35     extracted_x509_cert = get_x509_cert(xml_signed_root)
  36     verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml
  37 
  38 def sign_payload2():
  39     print "\n payload2"
  40     payload = open("payload2.xml").read()
  41     cert = open(PUB_KEY).read()
  42     key = open(PRIV_KEY).read()
  43     root = etree.fromstring(payload)
  44     signer = XMLSigner(method=methods.enveloped, signature_algorithm="rsa-sha256",
  45         digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
  46     signed_root = signer.sign(root, key=key, cert=cert, reference_uri="#tosign", id_attribute="id")
  47     xml_signed_root = etree.tostring(signed_root)
  48     xml_signed_root = xml_signed_root.replace("7676767", "1111111")  # introduce change in data not signed
  49     open("payload2_signed.xml", "wb").write(xml_signed_root)
  50     extracted_x509_cert = get_x509_cert(xml_signed_root)
  51     verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml
  52 
  53 def sign_payload2_detached_nocert_insignature():
  54     print "\n payload2 detached no cert in signature"
  55     payload = open("payload2.xml").read()
  56     cert = open(PUB_KEY).read()
  57     key = open(PRIV_KEY).read()
  58     root = etree.fromstring(payload)
  59     signer = XMLSigner(method=methods.detached, signature_algorithm="rsa-sha256",
  60         digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
  61     signature = signer.sign(root, key=key, cert=None, reference_uri="#tosign", id_attribute="id")
  62     root.insert(0, signature)  # insert as first child of the XML node
  63     xml_signed_root = etree.tostring(root)
  64     xml_signed_root = xml_signed_root.replace("7676767", "1111111")  # introduce change in data not signed
  65     open("payload2_signed_nocert.xml", "wb").write(xml_signed_root)
  66     verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=cert).signed_xml
  67 
  68 def sign_payload2_detached_cert_insignature():
  69     print "\n payload2 detached"
  70     payload = open("payload2.xml").read()
  71     cert = open(PUB_KEY).read()
  72     key = open(PRIV_KEY).read()
  73     root = etree.fromstring(payload)
  74     signer = XMLSigner(method=methods.detached, signature_algorithm="rsa-sha256",
  75         digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
  76     signature = signer.sign(root, key=key, cert=cert, reference_uri="#tosign", id_attribute="id")
  77     root.insert(0, signature)  # insert as first child of the root XML node
  78     xml_signed_root = etree.tostring(root)
  79     xml_signed_root = xml_signed_root.replace("7676767", "1111111")  # introduce change in data not signed
  80     open("payload2_signed_cert.xml", "wb").write(xml_signed_root)
  81     extracted_x509_cert = get_x509_cert(xml_signed_root)
  82     verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml
  83 
  84 def sign_test_detached_cert_insignature():
  85     print "\n test detached"
  86     payload = open("test.xml").read()
  87     cert = open(PUB_KEY).read()
  88     key = open(PRIV_KEY).read()
  89     root = etree.fromstring(payload)
  90     signer = XMLSigner(method=methods.detached, signature_algorithm="rsa-sha256",
  91         digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
  92     signature = signer.sign(root, key=key, cert=cert, reference_uri="#tosign", id_attribute="Id")
  93     nrelems = len(root.getchildren())
  94     root.insert(nrelems, signature)  # insert as the last child of the root XML node
  95     xml_signed_root = etree.tostring(root)
  96     open("test_signed.xml", "wb").write(xml_signed_root)
  97     extracted_x509_cert = get_x509_cert(xml_signed_root)
  98     verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml
  99 
 100 if __name__ == '__main__':
 101     sign_payload1()
 102     sign_payload2()
 103     sign_payload2_detached_nocert_insignature()
 104     sign_payload2_detached_cert_insignature()
 105     sign_test_detached_cert_insignature()

payload1.xml

   1 <a>
   2         <b>7676767</b>
   3 </a>

payload2.xml

   1 <a>
   2         <b>7676767</b>
   3         <c id="tosign">klfljsdfjlsdkfksflfk</c>
   4 </a>

test.xsd

   1 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
   2         xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
   3 
   4         <xs:import namespace="http://www.w3.org/2000/09/xmldsig#"
   5                 schemaLocation="./xmldsig-core-schema.xsd" />
   6 
   7         <xs:element name="a" type="AType" />
   8         <xs:complexType name="AType" mixed="true">
   9                 <xs:sequence>                   
  10                         <xs:element name="b" type="CType" />
  11                         <xs:element ref="ds:Signature" minOccurs="0" />
  12                 </xs:sequence>
  13         </xs:complexType>
  14 
  15         <xs:complexType name="CType" mixed="true">
  16                 <xs:sequence>
  17                         <xs:element name="c" type="xs:string" />
  18                 </xs:sequence>
  19                 <xs:attribute name="Id" type="xs:ID" use="optional" />
  20         </xs:complexType>
  21 </xs:schema>

test.xml

   1 <a>
   2         <b Id="tosign">
   3                 <c>test</c>
   4         </b>
   5 </a>

Check signed XML

   1 # check_signed_xml.py
   2 from signxml import XMLVerifier
   3 from lxml import etree
   4 import sys
   5 
   6 XML_SIG_NS = ns = {"ds":"http://www.w3.org/2000/09/xmldsig#"}
   7 X509_CERT = "//ds:X509Certificate"
   8 
   9 if __name__ == '__main__':
  10     print("Checking file %s" % (sys.argv[1]))
  11     xml_data = open(sys.argv[1]).read()
  12     try:
  13         extracted_x509_cert = etree.fromstring(xml_data).xpath(X509_CERT, namespaces=XML_SIG_NS)
  14         if len(extracted_x509_cert) > 0:
  15             verified_data = XMLVerifier().verify(xml_data, x509_cert=extracted_x509_cert[0].text).signed_xml
  16             print "Trusted data\n"+etree.tostring(verified_data)
  17         else:
  18             verified_data = XMLVerifier().verify(xml_data, require_x509=False).signed_xml
  19             print "Trusted data\n"+etree.tostring(verified_data)
  20         print("OK")
  21     except Exception as ex:
  22         print("Exception: %s" % (str(ex)))
  23         print("Has error")

Python/xmlsign (last edited 2024-05-30 00:56:47 by vitor)