= xmlsign =
 * https://pypi.org/project/signxml/
 * https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd

== Example ==

=== main.py ===
{{{#!highlight python
'''
pip install signxml lxml
# generate the x509 cert and private key
openssl req -new -x509 -days 3650 -nodes -out cert.pem -keyout privkey.pem

apt install libxml2-utils

xmllint --schema ./test.xsd ./test_signed.xml 
xmllint --schema ./test.xsd ./test.xml 

'''

from signxml import XMLVerifier, XMLSigner, methods, XMLSignatureProcessor
from lxml import etree

XML_SIG_NS = ns = {"ds":"http://www.w3.org/2000/09/xmldsig#"}
X509_CERT = "//ds:X509Certificate"
PUB_KEY = "cert.pem"
PRIV_KEY = "privkey.pem"

def get_x509_cert(xml_signed_root):
    assert(type(xml_signed_root) is str)
    extracted_x509_cert = etree.fromstring(xml_signed_root).xpath(X509_CERT, namespaces=XML_SIG_NS)[0].text
    return extracted_x509_cert

def sign_payload1():
    print "\n payload1"
    payload = open("payload1.xml").read()
    cert = open(PUB_KEY).read()
    key = open(PRIV_KEY).read()
    root = etree.fromstring(payload)
    signed_root = XMLSigner().sign(root, key=key, cert=cert)
    xml_signed_root = etree.tostring(signed_root)
    open("payload1_signed.xml", "wb").write(xml_signed_root)
    extracted_x509_cert = get_x509_cert(xml_signed_root)
    verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml

def sign_payload2():
    print "\n payload2"
    payload = open("payload2.xml").read()
    cert = open(PUB_KEY).read()
    key = open(PRIV_KEY).read()
    root = etree.fromstring(payload)
    signer = XMLSigner(method=methods.enveloped, signature_algorithm="rsa-sha256",
        digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
    signed_root = signer.sign(root, key=key, cert=cert, reference_uri="#tosign", id_attribute="id")
    xml_signed_root = etree.tostring(signed_root)
    xml_signed_root = xml_signed_root.replace("7676767", "1111111")  # introduce change in data not signed
    open("payload2_signed.xml", "wb").write(xml_signed_root)
    extracted_x509_cert = get_x509_cert(xml_signed_root)
    verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml

def sign_payload2_detached_nocert_insignature():
    print "\n payload2 detached no cert in signature"
    payload = open("payload2.xml").read()
    cert = open(PUB_KEY).read()
    key = open(PRIV_KEY).read()
    root = etree.fromstring(payload)
    signer = XMLSigner(method=methods.detached, signature_algorithm="rsa-sha256",
        digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
    signature = signer.sign(root, key=key, cert=None, reference_uri="#tosign", id_attribute="id")
    root.insert(0, signature)  # insert as first child of the XML node
    xml_signed_root = etree.tostring(root)
    xml_signed_root = xml_signed_root.replace("7676767", "1111111")  # introduce change in data not signed
    open("payload2_signed_nocert.xml", "wb").write(xml_signed_root)
    verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=cert).signed_xml

def sign_payload2_detached_cert_insignature():
    print "\n payload2 detached"
    payload = open("payload2.xml").read()
    cert = open(PUB_KEY).read()
    key = open(PRIV_KEY).read()
    root = etree.fromstring(payload)
    signer = XMLSigner(method=methods.detached, signature_algorithm="rsa-sha256",
        digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
    signature = signer.sign(root, key=key, cert=cert, reference_uri="#tosign", id_attribute="id")
    root.insert(0, signature)  # insert as first child of the root XML node
    xml_signed_root = etree.tostring(root)
    xml_signed_root = xml_signed_root.replace("7676767", "1111111")  # introduce change in data not signed
    open("payload2_signed_cert.xml", "wb").write(xml_signed_root)
    extracted_x509_cert = get_x509_cert(xml_signed_root)
    verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml

def sign_test_detached_cert_insignature():
    print "\n test detached"
    payload = open("test.xml").read()
    cert = open(PUB_KEY).read()
    key = open(PRIV_KEY).read()
    root = etree.fromstring(payload)
    signer = XMLSigner(method=methods.detached, signature_algorithm="rsa-sha256",
        digest_algorithm="sha256", c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm)
    signature = signer.sign(root, key=key, cert=cert, reference_uri="#tosign", id_attribute="Id")
    nrelems = len(root.getchildren())
    root.insert(nrelems, signature)  # insert as the last child of the root XML node
    xml_signed_root = etree.tostring(root)
    open("test_signed.xml", "wb").write(xml_signed_root)
    extracted_x509_cert = get_x509_cert(xml_signed_root)
    verified_data = XMLVerifier().verify(xml_signed_root, x509_cert=extracted_x509_cert).signed_xml

if __name__ == '__main__':
    sign_payload1()
    sign_payload2()
    sign_payload2_detached_nocert_insignature()
    sign_payload2_detached_cert_insignature()
    sign_test_detached_cert_insignature()
}}}

=== payload1.xml ===
{{{#!highlight xml
<a>
	<b>7676767</b>
</a>
}}}

=== payload2.xml ===
{{{#!highlight xml
<a>
	<b>7676767</b>
	<c id="tosign">klfljsdfjlsdkfksflfk</c>
</a>
}}}

=== test.xsd ===
{{{#!highlight xml
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
	xmlns:ds="http://www.w3.org/2000/09/xmldsig#">

	<xs:import namespace="http://www.w3.org/2000/09/xmldsig#"
		schemaLocation="./xmldsig-core-schema.xsd" />

	<xs:element name="a" type="AType" />
	<xs:complexType name="AType" mixed="true">
		<xs:sequence>			
			<xs:element name="b" type="CType" />
			<xs:element ref="ds:Signature" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="CType" mixed="true">
		<xs:sequence>
			<xs:element name="c" type="xs:string" />
		</xs:sequence>
		<xs:attribute name="Id" type="xs:ID" use="optional" />
	</xs:complexType>
</xs:schema>
}}}

=== test.xml ===
{{{#!highlight xml
<a>
	<b Id="tosign">
		<c>test</c>
	</b>
</a>
}}}

== Check signed XML ==
{{{#!highlight python
# check_signed_xml.py
from signxml import XMLVerifier
from lxml import etree
import sys

XML_SIG_NS = ns = {"ds":"http://www.w3.org/2000/09/xmldsig#"}
X509_CERT = "//ds:X509Certificate"

if __name__ == '__main__':
    print("Checking file %s" % (sys.argv[1]))
    xml_data = open(sys.argv[1]).read()
    try:
        extracted_x509_cert = etree.fromstring(xml_data).xpath(X509_CERT, namespaces=XML_SIG_NS)
        if len(extracted_x509_cert) > 0:
            verified_data = XMLVerifier().verify(xml_data, x509_cert=extracted_x509_cert[0].text).signed_xml
            print "Trusted data\n"+etree.tostring(verified_data)
        else:
            verified_data = XMLVerifier().verify(xml_data, require_x509=False).signed_xml
            print "Trusted data\n"+etree.tostring(verified_data)
        print("OK")
    except Exception as ex:
        print("Exception: %s" % (str(ex)))
        print("Has error")

}}}