Python XML Security Library Bindings
The `xmlsec` library provides Python bindings for the XML Security Library, enabling XML digital signatures, encryption, and other security operations on XML documents. It is actively maintained with frequent updates, often focused on ensuring compatibility with specific versions of the underlying `lxml` and `libxml2` libraries. The current version is 1.3.17.
Warnings
- breaking Binary wheels for `xmlsec` have strict version requirements for `lxml` due to shared underlying `libxml2`. Installing incompatible versions will lead to runtime errors like `ImportError: cannot import name _xmlsec from lxml.etree` or segfaults.
- gotcha When installing `xmlsec` from source (e.g., if no wheel is available for your platform/Python version), it requires the C libraries `libxml2` and `libxmlsec1` (including their development headers) to be installed on your system. Failure to do so will result in build errors.
- gotcha It is crucial to initialize the `xmlsec` library by calling `xmlsec.init()` at the start of your application and to properly clean up resources by calling `xmlsec.shutdown()` before your application exits. Failing to do so can lead to memory leaks or undefined behavior.
Install
-
pip install xmlsec
Imports
- xmlsec
import xmlsec
- etree
from lxml import etree
Quickstart
import xmlsec
from lxml import etree
import os
# Initialize xmlsec library
xmlsec.init()
# Define paths for key and certificate files
# In a real application, replace these with your actual key and cert paths.
# For this example to run, ensure these files exist or create dummy ones:
# Example: openssl genrsa -out private_key.pem 2048
# Example: openssl req -new -x509 -key private_key.pem -out certificate.pem -days 365
key_file = os.environ.get('XMLSEC_PRIVATE_KEY_PATH', 'private_key.pem')
cert_file = os.environ.get('XMLSEC_CERTIFICATE_PATH', 'certificate.pem')
# Basic check for dummy files for quickstart execution
if not os.path.exists(key_file) or not os.path.exists(cert_file):
print(f"Warning: '{key_file}' and '{cert_file}' not found. "
"Please generate dummy key/cert files for this quickstart to run.")
# Minimal dummy content for illustration, DO NOT USE IN PRODUCTION
with open(key_file, 'w') as f: f.write("-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n")
with open(cert_file, 'w') as f: f.write("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n")
try:
# Create an XML document using lxml
root = etree.Element("Data")
etree.SubElement(root, "Item", id="item1").text = "Value1"
# Add a signature template to the root element
signature_node = xmlsec.template.add_signature(root, xmlsec.constants.TransformExclC14N)
# Add SignedInfo element, reference the entire document
signed_info_node = xmlsec.template.ensure_signed_info(signature_node,
xmlsec.constants.TransformExclC14N,
xmlsec.constants.TransformRsaSha1)
xmlsec.template.add_reference(signed_info_node, xmlsec.constants.TransformSha1, uri="#xpointer(/)")
# Add KeyInfo element with certificate
key_info_node = xmlsec.template.ensure_key_info(signature_node)
xmlsec.template.add_x509_data(key_info_node)
xmlsec.template.add_x509_certificate(key_info_node) # Include the certificate itself
# Create a KeysManager and load the private key for signing
manager = xmlsec.KeysManager()
signing_key = xmlsec.Key.from_file(key_file, xmlsec.KeyFormatPEM)
# Add the certificate to the key object for inclusion in KeyInfo
signing_key.add_cert_from_file(cert_file, xmlsec.KeyFormatPEM)
manager.add_key(signing_key)
# Create a SignatureContext and set the signing key
ctx = xmlsec.SignatureContext(manager)
ctx.key = signing_key
# Sign the XML document
print("Signing XML document...")
ctx.sign(signature_node)
signed_xml_str = etree.tostring(root, pretty_print=True, encoding='unicode')
print("Signed XML:\n", signed_xml_str)
# Verify the signature
print("\nVerifying signature...")
verify_ctx = xmlsec.SignatureContext(manager) # Use the same manager with loaded key/cert
if verify_ctx.verify(signature_node):
print("Signature is valid!")
else:
print("Signature verification FAILED.")
finally:
# Shutdown xmlsec library
xmlsec.shutdown()