### Install SignXML Source: https://xml-security.github.io/signxml/index.html Install the package using pip. ```bash pip install signxml ``` -------------------------------- ### Get X509 Certificate Chain Verifier Source: https://xml-security.github.io/signxml/_modules/signxml/verifier.html Instantiates an X509CertChainVerifier with a provided CA PEM file. This is used for verifying certificate chains. ```python def get_cert_chain_verifier(self, ca_pem_file): return X509CertChainVerifier(ca_pem_file=ca_pem_file) ``` -------------------------------- ### Get Canonicalization Inputs from References Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Resolves references to canonicalization inputs and updates URI fragments. ```python def _get_c14n_inputs_from_references(self, doc_root, references: List[SignatureReference]): c14n_inputs, new_references = [], [] for reference in references: uri = reference.URI if reference.URI.startswith("#") else "#" + reference.URI c14n_inputs.append(self.get_root(self._resolve_reference(doc_root, {"URI": uri}))) new_references.append(replace(reference, URI=uri)) return c14n_inputs, new_references ``` -------------------------------- ### Initialize XMLSigner with Custom Algorithms Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Instantiate an XMLSigner with specific signature, digest, and canonicalization algorithms. Ensure algorithms are valid or provided in the correct format. ```python def __init__( self, method: SignatureConstructionMethod = SignatureConstructionMethod.enveloped, signature_algorithm: Union[SignatureMethod, str] = SignatureMethod.RSA_SHA256, digest_algorithm: Union[DigestAlgorithm, str] = DigestAlgorithm.SHA256, c14n_algorithm: Union[CanonicalizationMethod, str] = CanonicalizationMethod.CANONICAL_XML_1_1, ): if method is None or method not in SignatureConstructionMethod: raise InvalidInput(f"Unknown signature construction method {method}") self.construction_method = method if isinstance(signature_algorithm, str) and "#" not in signature_algorithm: self.sign_alg = SignatureMethod.from_fragment(signature_algorithm) else: self.sign_alg = SignatureMethod(signature_algorithm) if isinstance(digest_algorithm, str) and "#" not in digest_algorithm: self.digest_alg = DigestAlgorithm.from_fragment(digest_algorithm) else: self.digest_alg = DigestAlgorithm(digest_algorithm) self.check_deprecated_methods() self.c14n_alg = CanonicalizationMethod(c14n_algorithm) self.namespaces = dict(ds=namespaces.ds) self._parser = None self.signature_annotators = [self._add_key_info] ``` -------------------------------- ### Initial Elements of ws-security Support Source: https://xml-security.github.io/signxml/changelog.html Introduces the foundational elements for supporting WS-Security standards within the library. ```python Initial elements of ws-security support ``` -------------------------------- ### Add Custom Key Info When Signing Source: https://xml-security.github.io/signxml/changelog.html Supports providing custom key information when signing XML documents. ```python Support custom key info when signing ``` -------------------------------- ### Build Signed Info Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Constructs the SignedInfo element, including CanonicalizationMethod, SignatureMethod, and all Reference elements. Handles inclusive namespaces. ```python def _build_sig(self, sig_root, references, c14n_inputs, inclusive_ns_prefixes, exclude_c14n_transform_element=False): signed_info = SubElement(sig_root, self._ds_tag("SignedInfo"), nsmap=self.namespaces) sig_c14n_method = SubElement(signed_info, self._ds_tag("CanonicalizationMethod"), Algorithm=self.c14n_alg.value) if inclusive_ns_prefixes: SubElement(sig_c14n_method, ec_tag("InclusiveNamespaces"), PrefixList=" ".join(inclusive_ns_prefixes)) SubElement(signed_info, self._ds_tag("SignatureMethod"), Algorithm=self.sign_alg.value) for i, reference in enumerate(references): if reference.c14n_method is None: reference = replace(reference, c14n_method=self.c14n_alg) if reference.inclusive_ns_prefixes is None: reference = replace(reference, inclusive_ns_prefixes=inclusive_ns_prefixes) reference_node = SubElement(signed_info, self._ds_tag("Reference"), URI=reference.URI) transforms = SubElement(reference_node, self._ds_tag("Transforms")) self._build_transforms_for_reference( transforms_node=transforms, reference=reference, exclude_c14n_transform_element=exclude_c14n_transform_element, ) SubElement(reference_node, self._ds_tag("DigestMethod"), Algorithm=self.digest_alg.value) digest_value = SubElement(reference_node, self._ds_tag("DigestValue")) payload_c14n = self._c14n( c14n_inputs[i], algorithm=reference.c14n_method, inclusive_ns_prefixes=reference.inclusive_ns_prefixes ) digest = self._get_digest(payload_c14n, algorithm=self.digest_alg) digest_value.text = b64encode(digest).decode() signature_value = SubElement(sig_root, self._ds_tag("SignatureValue")) return signed_info, signature_value ``` -------------------------------- ### Initialize Signature Verification Source: https://xml-security.github.io/signxml/_modules/signxml/verifier.html Configures the verification environment, including HMAC key validation and signature configuration updates. ```python self.hmac_key = hmac_key if hmac_key is not None: if expect_config.signature_methods == SignatureConfiguration().signature_methods: # default value expect_config = replace( expect_config, signature_methods=frozenset(sm for sm in SignatureMethod if sm.name.startswith("HMAC")), ) elif any(not sm.name.startswith("HMAC") for sm in expect_config.signature_methods): raise InvalidInput("When hmac_key is set, all expected signature methods must use HMAC") self.config = expect_config if deprecated_kwargs: self.config = replace(expect_config, **deprecated_kwargs) self._parser = parser if x509_cert or cert_resolver: self.config = replace(self.config, require_x509=True) if x509_cert and str(type(x509_cert)) == "": warn( "SignXML received a PyOpenSSL object as x509_cert input. Please pass a Cryptography.X509 object instead.", DeprecationWarning, ) x509_cert = x509_cert.to_cryptography() # type: ignore[union-attr] self.x509_cert = x509_cert if id_attribute is not None: self.id_attributes = (id_attribute,) ``` -------------------------------- ### XMLSigner Initialization Source: https://xml-security.github.io/signxml/index.html Initialize an XMLSigner object to configure signing parameters. ```APIDOC ## XMLSigner Initialization ### Description Create a new XML Signature Signer object, which can be used to hold configuration information and sign multiple pieces of data. ### Parameters - **method** (SignatureConstructionMethod) - The method for constructing the signature (enveloped, enveloping, or detached). - **signature_algorithm** (SignatureMethod or str) - The algorithm used to generate the signature. - **digest_algorithm** (DigestAlgorithm or str) - The algorithm used to hash data during signature generation. - **c14n_algorithm** (CanonicalizationMethod or str) - The algorithm used to canonicalize XML before signing. ``` -------------------------------- ### Configure Namespace Prefixes Source: https://xml-security.github.io/signxml/index.html Sets custom namespace mappings for the XML signer to ensure compatibility with specific application requirements. ```python signer = signxml.XMLSigner(...) signer.namespaces = {None: signxml.namespaces.ds} signed_root = signer.sign(...) ``` -------------------------------- ### Support Signing and Verifying Multiple References Source: https://xml-security.github.io/signxml/changelog.html This version adds support for signing and verifying XML documents with multiple references. ```python Support signing and verifying multiple references ``` -------------------------------- ### Accept PEM Keys as String or Bytes Source: https://xml-security.github.io/signxml/changelog.html PEM keys can now be accepted as either string or bytes, providing flexibility in input format. ```python Accept PEM keys as either str or bytes ``` -------------------------------- ### Support Custom Inclusive Namespace Prefixes Source: https://xml-security.github.io/signxml/changelog.html Allows specifying custom `inclusive_ns_prefixes` when signing XML documents, providing more control over namespace inclusion. ```python Support custom inclusive_ns_prefixes when signing ``` -------------------------------- ### Configure Signature Validation Source: https://xml-security.github.io/signxml/index.html Initialize signature settings to define expected methods, algorithms, and security requirements. ```python class signxml.SignatureConfiguration(_require_x509 =True_, _location ='.//'_, _expect_references =1_, _signature_methods =frozenset({SignatureMethod.DSA_SHA256, SignatureMethod.ECDSA_SHA224, SignatureMethod.ECDSA_SHA256, SignatureMethod.ECDSA_SHA384, SignatureMethod.ECDSA_SHA3_224, SignatureMethod.ECDSA_SHA3_256, SignatureMethod.ECDSA_SHA3_384, SignatureMethod.ECDSA_SHA3_512, SignatureMethod.ECDSA_SHA512, SignatureMethod.HMAC_SHA224, SignatureMethod.HMAC_SHA256, SignatureMethod.HMAC_SHA384, SignatureMethod.HMAC_SHA512, SignatureMethod.RSA_SHA224, SignatureMethod.RSA_SHA256, SignatureMethod.RSA_SHA384, SignatureMethod.RSA_SHA512, SignatureMethod.SHA224_RSA_MGF1, SignatureMethod.SHA256_RSA_MGF1, SignatureMethod.SHA384_RSA_MGF1, SignatureMethod.SHA3_224_RSA_MGF1, SignatureMethod.SHA3_256_RSA_MGF1, SignatureMethod.SHA3_384_RSA_MGF1, SignatureMethod.SHA3_512_RSA_MGF1, SignatureMethod.SHA512_RSA_MGF1})_, _digest_algorithms =frozenset({DigestAlgorithm.SHA224, DigestAlgorithm.SHA256, DigestAlgorithm.SHA384, DigestAlgorithm.SHA3_224, DigestAlgorithm.SHA3_256, DigestAlgorithm.SHA3_384, DigestAlgorithm.SHA3_512, DigestAlgorithm.SHA512})_, _ignore_ambiguous_key_info =False_, _default_reference_c14n_method =CanonicalizationMethod.CANONICAL_XML_1_1_)[source] ``` -------------------------------- ### Sign and Verify XML Source: https://xml-security.github.io/signxml/index.html Use XMLSigner to sign an XML element and XMLVerifier to validate the signature. ```python from lxml import etree from signxml import XMLSigner, XMLVerifier data_to_sign = "" cert = open("cert.pem").read() key = open("privkey.pem").read() root = etree.fromstring(data_to_sign) signed_root = XMLSigner().sign(root, key=key, cert=cert) verified_data = XMLVerifier().verify(signed_root).signed_xml ``` -------------------------------- ### SignXML Configuration Settings Source: https://xml-security.github.io/signxml/index.html Configuration parameters for controlling signature verification behavior and security constraints. ```APIDOC ## Configuration Settings ### Description Settings to control how SignXML handles signature verification, including security checks for ambiguous keys and default canonicalization methods. ### Parameters - **ignore_ambiguous_key_info** (bool) - Optional - When False, compares KeyValue against X.509 certificate and raises InvalidInput on mismatch. Set to True to ignore KeyValue and validate using X509Data only. - **default_reference_c14n_method** (CanonicalizationMethod) - Optional - The default canonicalization method used for referenced data structures if no algorithm is specified in the Transforms element. ``` -------------------------------- ### Add pyinstaller Support Source: https://xml-security.github.io/signxml/changelog.html Includes support for `pyinstaller`, facilitating the packaging of applications that use this library. ```python Add pyinstaller support to signxml (#188) ``` -------------------------------- ### verify(data, *, expect_signature_policy=None, expect_config=..., **xml_verifier_args) Source: https://xml-security.github.io/signxml/index.html Verifies a XAdES signature supplied in the data and returns a list of XAdESVerifyResult structures. ```APIDOC ## verify(data, *, expect_signature_policy=None, expect_config=..., **xml_verifier_args) ### Description Verify the XAdES signature supplied in the data and return a list of `XAdESVerifyResult` data structures representing the data signed by the signature, or raise an exception if the signature is not valid. This method is a wrapper around `signxml.XMLVerifier.verify()`. ### Parameters #### Request Body - **expect_signature_policy** (XAdESSignaturePolicy | None) - Optional - Assert that the verified XAdES signature carries specific data in the SignaturePolicyIdentifier element. - **expect_config** (XAdESSignatureConfiguration) - Optional - Expected signature configuration. Signatures with unexpected configurations will fail validation. - **xml_verifier_args** (dict) - Optional - Parameters to pass to `signxml.XMLVerifier.verify()`. ### Response #### Success Response (200) - **List[XAdESVerifyResult]** - A list of verification results. ``` -------------------------------- ### Sign with RSA (PKCS1v15) Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Signs the data using RSA with PKCS1v15 padding and a specified hash algorithm. Requires the private key to be loaded. ```python elif self.sign_alg.name.startswith("RSA_"): signature = signing_settings.key.sign(signed_info_c14n, padding=PKCS1v15(), algorithm=hash_alg) ``` -------------------------------- ### Create DS Namespace QName Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Helper method to create a QName object for the 'ds' namespace, respecting configured namespace mappings. Useful for constructing XML elements within the DSIG namespace. ```python def _ds_tag(self, tag): """ Create a QName for the ds namespace, respecting the configured namespace mapping. When the default namespace is set to the ds namespace ({None: namespaces.ds}), ``` -------------------------------- ### Match Public Key Values Source: https://xml-security.github.io/signxml/_modules/signxml/verifier.html Compares public key information from XML signature data (KeyValue and DEREncodedKeyValue) against the public key of a signing certificate. Raises an error if mismatches are found and ignore_ambiguous_key_info is not set to True. ```python def _match_key_values(self, key_value, der_encoded_key_value, signing_cert, signature_alg): if self.config.ignore_ambiguous_key_info is True: return cert_pub_key = signing_cert.public_key() # If both X509Data and KeyValue are present, match one against the other and raise an error on mismatch if key_value is not None: match_result = self._check_key_value_matches_cert_public_key(key_value, cert_pub_key, signature_alg) if match_result is False: raise InvalidInput( "Both X509Data and KeyValue found and they represent different public keys. " "Use verify(ignore_ambiguous_key_info=True) to ignore KeyValue and validate " "using X509Data only." ) # If both X509Data and DEREncodedKeyValue are present, match one against the other and raise an error on # mismatch if der_encoded_key_value is not None: match_result = self._check_der_key_value_matches_cert_public_key( der_encoded_key_value, signing_cert.public_key(), signature_alg ) if match_result is False: raise InvalidInput( "Both X509Data and DEREncodedKeyValue found and they represent different " "public keys. Use verify(ignore_ambiguous_key_info=True) to ignore " "DEREncodedKeyValue and validate using X509Data only." ) ``` -------------------------------- ### Define Signature Construction Methods Source: https://xml-security.github.io/signxml/_modules/signxml/algorithms.html Enumeration of supported XML signature construction methods used during the signing process. ```python class SignatureConstructionMethod(Enum): """ An enumeration of signature construction methods supported by SignXML, used to specify the method when signing. See the list of signature types under `XML Signature Syntax and Processing Version 2.0, Definitions `_. """ enveloped = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" """ The signature is over the XML content that contains the signature as an element. The content provides the root XML document element. This is the most common XML signature type in modern applications. """ enveloping = "enveloping-signature" """ The signature is over content found within an Object element of the signature itself. The Object (or its content) is identified via a Reference (via a URI fragment identifier or transform). """ detached = "detached-signature" """ The signature is over content external to the Signature element, and can be identified via a URI or transform. Consequently, the signature is "detached" from the content it signs. This definition typically applies to separate data objects, but it also includes the instance where the Signature and data object reside within the same XML document but are sibling elements. """ ``` -------------------------------- ### Initialize XAdES Signer Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Signer class for creating XAdES signatures with support for policies, roles, and data formats. ```python class XAdESSigner(XAdESProcessor, XMLSigner): """ Create a new XAdES Signature Signer object, which can be used to hold configuration information and sign multiple pieces of data. This is a subclass of :class:`signxml.XMLSigner`; all of its configuration semantics are supported. :param signature_policy: If you need your XAdES signature to carry the **SignaturePolicyIdentifier** element, use this parameter to pass a :class:`XAdESSignaturePolicy` object carrying strings and the digest method identifier for the element. :param claimed_roles: If you need your XAdES signature to carry the **SignerRole/ClaimedRoles** element, use this parameter to pass a list of strings to use as text for the **ClaimedRole** tags. :param data_object_format: If you need your XAdES signature to carry the **DataObjectFormat** element, use this parameter to pass a :class:`XAdESDataObjectFormat` object carrying the Description and MimeType strings for the element. :param xml_signer_args: Parameters to pass to the :class:`signxml.XMLSigner` constructor. """ use_deprecated_legacy_signing_certificate: bool = False def __init__( self, signature_policy: Optional[XAdESSignaturePolicy] = None, claimed_roles: Optional[List] = None, data_object_format: Optional[XAdESDataObjectFormat] = None, **xml_signer_args, ) -> None: super().__init__(**xml_signer_args) if self.sign_alg.name.startswith("HMAC_"): raise Exception("HMAC signatures are not supported by XAdES") self.signature_annotators.append(self._build_xades_ds_object) self._tokens_used: Dict[str, bool] = {} self.signed_signature_properties_annotators = [ self.add_signing_time, self.add_signing_certificate, self.add_signature_policy_identifier, self.add_signature_production_place, self.add_signer_role, ``` -------------------------------- ### Verify XML Signature with Custom Configuration Source: https://xml-security.github.io/signxml/_modules/signxml/verifier.html Use this snippet to verify an XML signature with a specific configuration, such as enforcing a particular signature location. Ensure the `expect_config` is set appropriately for your security requirements. ```python from signxml import XMLVerifier, SignatureConfiguration config = SignatureConfiguration(location="./") XMLVerifier(...).verify(..., expect_config=config) ``` -------------------------------- ### Generate Signed Info Node Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Builds the SignedInfo node and the SignatureValue node, which are core components of an XML signature. ```python signed_info_node, signature_value_node = self._build_sig( sig_root, references=references, c14n_inputs=c14n_inputs, inclusive_ns_prefixes=inclusive_ns_prefixes, exclude_c14n_transform_element=exclude_c14n_transform_element, ) ``` -------------------------------- ### Include Both X509Data and KeyValue in Signature Source: https://xml-security.github.io/signxml/changelog.html Use the `always_add_key_value` keyword argument in `XMLSigner.sign()` to include both X509Data and KeyValue. This is useful for signing applications that require both. ```python XMLSigner.sign(): add always_add_key_value kwarg to include both X509Data and KeyValue for ill-defined signing applications ``` -------------------------------- ### Load Private Key for Signing Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Loads a PEM-encoded private key for signing operations. Handles string or bytes input and requires a passphrase if the key is encrypted. ```python if isinstance(key, (str, bytes)): signing_settings.key = load_pem_private_key(ensure_bytes(key), password=passphrase) ``` -------------------------------- ### Add Data Object Format Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Initializes the addition of data object format properties by locating the SignedInfo element. ```python def add_data_object_format(self, signed_data_object_properties, sig_root, signing_settings: SigningSettings): signed_info = self._find(sig_root, "ds:SignedInfo") ``` -------------------------------- ### Construct XAdES Signing Certificate Elements Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Builds the SigningCertificate structure, including legacy CertDigest and IssuerSerial nodes, as well as the current SigningCertificateV2 default. ```python signed_signature_properties, xades_tag("SigningCertificate"), nsmap=self.namespaces ) cert_node_legacy = SubElement(signing_cert, xades_tag("Cert"), nsmap=self.namespaces) cert_digest = SubElement(cert_node_legacy, xades_tag("CertDigest"), nsmap=self.namespaces) SubElement( cert_digest, ds_tag("DigestMethod"), nsmap=self.namespaces, Algorithm=DigestAlgorithm.SHA1.value ) digest_value_node = SubElement(cert_digest, ds_tag("DigestValue"), nsmap=self.namespaces) digest_value_node.text = b64encode(cert_digest_sha1_bytes).decode() issuer_serial = SubElement(cert_node_legacy, xades_tag("IssuerSerial"), nsmap=self.namespaces) issuer_name = SubElement(issuer_serial, ds_tag("X509IssuerName"), nsmap=self.namespaces) issuer_name.text = "C={C},O={O},OU={OU},CN={CN}".format( # type:ignore[str-bytes-safe] C=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.COUNTRY_NAME)[0].value, O=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATION_NAME)[0].value, OU=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATIONAL_UNIT_NAME)[0].value, CN=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value, ) serial_number = SubElement(issuer_serial, ds_tag("X509SerialNumber"), nsmap=self.namespaces) serial_number.text = str(loaded_cert.serial_number) # SigningCertificateV2 (current default) cert_node = SubElement(signing_cert_v2, xades_tag("Cert"), nsmap=self.namespaces) cert_digest = SubElement(cert_node, xades_tag("CertDigest"), nsmap=self.namespaces) SubElement(cert_digest, ds_tag("DigestMethod"), nsmap=self.namespaces, Algorithm=self.digest_alg.value) digest_value_node = SubElement(cert_digest, ds_tag("DigestValue"), nsmap=self.namespaces) digest_value_node.text = b64encode(cert_digest_bytes).decode() # issuer_serial_number = loaded_cert.get_serial_number() # issuer_serial_bytes = long_to_bytes(issuer_serial_number) # issuer_serial_v2 = SubElement(cert_node, xades_tag("IssuerSerialV2"), nsmap=self.namespaces) # issuer_serial_v2.text = b64encode(issuer_serial_bytes).decode() ``` -------------------------------- ### SignatureConfiguration Class Source: https://xml-security.github.io/signxml/index.html Documentation for the SignatureConfiguration class, used to configure signature parameters. ```APIDOC ## SignatureConfiguration Configures parameters for XML signatures. ### Properties - **`require_x509`** (boolean) - Description: Whether X.509 certificates are required. - **`location`** (string) - Description: The location for the signature. - **`expect_references`** (list) - Description: Expected references in the signature. - **`signature_methods`** (list) - Description: Allowed signature methods. - **`digest_algorithms`** (list) - Description: Allowed digest algorithms. - **`ignore_ambiguous_key_info`** (boolean) - Description: Whether to ignore ambiguous key info. - **`default_reference_c14n_method`** (string) - Description: The default canonicalization method for references. ``` -------------------------------- ### Ignore Ambiguous Key Info During XML Verification Source: https://xml-security.github.io/signxml/changelog.html Use the `ignore_ambiguous_key_info` keyword argument in `XMLVerifier.verify()` to bypass the default rejection of signatures containing both X509Data and KeyValue. This allows verification of ambiguous key information. ```python XMLVerifier.verify(): reject signatures that contain both X509Data and KeyValue by default; add ignore_ambiguous_key_info kwarg to bypass ``` -------------------------------- ### Verify SAML Assertions Source: https://xml-security.github.io/signxml/index.html Extracts an X.509 certificate from metadata and verifies a SAML assertion. ```python from lxml import etree from base64 import b64decode from signxml import XMLVerifier with open("metadata.xml", "rb") as fh: cert = etree.parse(fh).find("//ds:X509Certificate").text assertion_data = XMLVerifier().verify(b64decode(assertion_body), x509_cert=cert).signed_xml ``` -------------------------------- ### SignatureConfiguration Source: https://xml-security.github.io/signxml/index.html Configuration settings for asserting properties of an XML signature during verification. ```APIDOC ## SignatureConfiguration ### Description A container holding signature settings used to assert properties of the signature during the verification process. ### Configuration Parameters - **require_x509** (bool) - If True, requires a valid X.509 certificate-based signature. - **location** (str) - XPath location where the signature tag is expected. - **expect_references** (int | bool) - Number of references to expect in the signature. - **signature_methods** (FrozenSet) - Set of acceptable signature algorithms. - **digest_algorithms** (FrozenSet) - Set of acceptable digest algorithms. ``` -------------------------------- ### Build Signature Reference Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Constructs a Reference node within the SignedInfo element, including Transforms and DigestMethod. Handles canonicalization and digest calculation. ```python if reference.c14n_method is None: reference = replace(reference, c14n_method=self.c14n_alg) if reference.inclusive_ns_prefixes is None: reference = replace(reference, inclusive_ns_prefixes=inclusive_ns_prefixes) reference_node = SubElement(signed_info, self._ds_tag("Reference"), URI=reference.URI) transforms = SubElement(reference_node, self._ds_tag("Transforms")) self._build_transforms_for_reference( transforms_node=transforms, reference=reference, exclude_c14n_transform_element=exclude_c14n_transform_element, ) SubElement(reference_node, self._ds_tag("DigestMethod"), Algorithm=self.digest_alg.value) digest_value = SubElement(reference_node, self._ds_tag("DigestValue")) payload_c14n = self._c14n( c14n_inputs[i], algorithm=reference.c14n_method, inclusive_ns_prefixes=reference.inclusive_ns_prefixes ) digest = self._get_digest(payload_c14n, algorithm=self.digest_alg) digest_value.text = b64encode(digest).decode() ``` -------------------------------- ### Add InclusiveNamespaces PrefixList Support for SignedInfo Source: https://xml-security.github.io/signxml/changelog.html Adds support for specifying `PrefixList` within `InclusiveNamespaces` for `SignedInfo`, enhancing control over namespace handling during signing. ```python Add InclusiveNamespaces PrefixList support for SignedInfo ``` -------------------------------- ### Verify SHA1 Signatures (Legacy) Source: https://xml-security.github.io/signxml/index.html Use this configuration to verify SHA1-based signatures, which are deprecated and disabled by default. Ensure the SignatureConfiguration includes the necessary signature and digest algorithms. ```python XMLVerifier().verify( expect_config=SignatureConfiguration( signature_methods=..., digest_algorithms=... ) ) ``` -------------------------------- ### Sign XML Document with XAdES Source: https://xml-security.github.io/signxml/index.html Use XAdESSigner to sign an XML document with XAdES metadata. Requires a private key and certificate. Ensure the correct canonicalization algorithm is specified. ```python from signxml import DigestAlgorithm from signxml.xades import ( XAdESSigner, XAdESSignaturePolicy, XAdESDataObjectFormat, ) signature_policy = XAdESSignaturePolicy( Identifier="MyPolicyIdentifier", Description="Hello XAdES", DigestMethod=DigestAlgorithm.SHA256, DigestValue="Ohixl6upD6av8N7pEvDABhEL6hM=", ) data_object_format = XAdESDataObjectFormat( Description="My XAdES signature", MimeType="text/xml", ) signer = XAdESSigner( signature_policy=signature_policy, claimed_roles=["signer"], data_object_format=data_object_format, c14n_algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315", ) signed_doc = signer.sign(doc, key=private_key, cert=certificate) ``` -------------------------------- ### POST /verify Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Verifies a XAdES signature supplied in the data and returns a list of XAdESVerifyResult structures. ```APIDOC ## POST /verify ### Description Verifies the XAdES signature supplied in the data. This method acts as a wrapper around the standard XMLVerifier.verify method and validates that the signature contains the required XAdES properties. ### Method POST ### Parameters #### Request Body - **data** (binary/xml) - Required - The XML data containing the XAdES signature to verify. - **expect_signature_policy** (XAdESSignaturePolicy) - Optional - Asserts that the signature carries specific data in the SignaturePolicyIdentifier element. - **expect_config** (XAdESSignatureConfiguration) - Optional - Configuration object describing expected properties of the signature. Defaults to XAdESSignatureConfiguration(). - **xml_verifier_args** (dict) - Optional - Additional arguments to pass to the underlying XMLVerifier.verify method. ### Response #### Success Response (200) - **List[XAdESVerifyResult]** - A list of data structures representing the data signed by the signature. ### Errors - **InvalidInput**: Raised if the signature is not valid, if X509 is not required, or if the xades:SignedProperties element is missing. ``` -------------------------------- ### Implement Lookup and Error Handling Mixins Source: https://xml-security.github.io/signxml/_modules/signxml/algorithms.html Helper classes to facilitate fragment-based lookups and standardized error handling for enumerations. ```python class FragmentLookupMixin: @classmethod def from_fragment(cls, fragment): for i in cls: # type: ignore[attr-defined] if i.value.endswith("#" + fragment): return i else: raise InvalidInput(f"Unrecognized {cls.__name__} identifier fragment: {fragment}") class InvalidInputErrorMixin: @classmethod def _missing_(cls, value): raise InvalidInput(f"Unrecognized {cls.__name__}: {value}") def __repr__(self): return f"{self.__class__.__name__}.{self.name}" # type: ignore[attr-defined] ``` -------------------------------- ### Unpack Signature Data Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Initializes the signature root and handles logic for enveloped and detached signature construction methods. ```python def _unpack(self, data, references: List[SignatureReference]): sig_root = Element(self._ds_tag("Signature"), nsmap=self.namespaces) if self.construction_method == SignatureConstructionMethod.enveloped: if isinstance(data, (str, bytes)): raise InvalidInput("When using enveloped signature, **data** must be an XML element") doc_root = self.get_root(data) c14n_inputs = [self.get_root(data)] if references is not None: # Only sign the referenced element(s) c14n_inputs, references = self._get_c14n_inputs_from_references(doc_root, references) signature_placeholders = self._findall(doc_root, "Signature[@Id='placeholder']", xpath=".//") if len(signature_placeholders) == 0: doc_root.append(sig_root) elif len(signature_placeholders) == 1: sig_root = signature_placeholders[0] del sig_root.attrib["Id"] for c14n_input in c14n_inputs: placeholders = self._findall(c14n_input, "Signature[@Id='placeholder']", xpath=".//") if placeholders: assert len(placeholders) == 1 _remove_sig(placeholders[0]) else: raise InvalidInput("Enveloped signature input contains more than one placeholder") if references is None: # Set default reference URIs based on signed data ID attribute values references = [] for c14n_input in c14n_inputs: payload_id = c14n_input.get("Id", c14n_input.get("ID")) uri = "#{}".format(payload_id) if payload_id is not None else "" references.append(SignatureReference(URI=uri)) elif self.construction_method == SignatureConstructionMethod.detached: doc_root = self.get_root(data) if references is None: uri = "#{}".format(data.get("Id", data.get("ID", "object"))) references = [SignatureReference(URI=uri)] c14n_inputs = [self.get_root(data)] ``` -------------------------------- ### Signing XML Data Source: https://xml-security.github.io/signxml/index.html The `sign` method is used to sign XML data. It supports various parameters for specifying the signing key, certificate, reference URIs, and key information. ```python signed = signer.sign(data, ...) ``` -------------------------------- ### Add Key Information to Signature Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Serializes key information into the XML signature root, handling HMAC algorithms and X.509 certificate chains. ```python def _add_key_info(self, sig_root, signing_settings: SigningSettings): if self.sign_alg.name.startswith("HMAC_"): return if signing_settings.key_info is None: key_info = SubElement(sig_root, self._ds_tag("KeyInfo")) if signing_settings.key_name is not None: keyname = SubElement(key_info, self._ds_tag("KeyName")) keyname.text = signing_settings.key_name if signing_settings.cert_chain is None or signing_settings.always_add_key_value: self._serialize_key_value(signing_settings.key, key_info) if signing_settings.cert_chain is not None: assert len(signing_settings.cert_chain) > 0 x509_data = SubElement(key_info, self._ds_tag("X509Data")) for cert in signing_settings.cert_chain: x509_certificate = SubElement(x509_data, self._ds_tag("X509Certificate")) if isinstance(cert, (str, bytes)): x509_certificate.text = strip_pem_header(cert) else: x509_certificate.text = strip_pem_header(cert.public_bytes(Encoding.PEM)) else: sig_root.append(signing_settings.key_info) ``` -------------------------------- ### Signature Construction Methods Source: https://xml-security.github.io/signxml/index.html An enumeration of signature construction methods supported by SignXML, used to specify the method when signing. ```APIDOC ## Signature Construction Methods An enumeration of signature construction methods supported by SignXML, used to specify the method when signing. See the list of signature types under XML Signature Syntax and Processing Version 2.0, Definitions. ### Supported Signature Construction Methods * **enveloped**: `http://www.w3.org/2000/09/xmldsig#enveloped-signature` The signature is over the XML content that contains the signature as an element. The content provides the root XML document element. This is the most common XML signature type in modern applications. * **enveloping**: `enveloping-signature` The signature is over content found within an Object element of the signature itself. The Object (or its content) is identified via a Reference (via a URI fragment identifier or transform). * **detached**: `detached-signature` The signature is over content external to the Signature element, and can be identified via a URI or transform. Consequently, the signature is “detached” from the content it signs. This definition typically applies to separate data objects, but it also includes the instance where the Signature and data object reside within the same XML document but are sibling elements. `signxml.methods` is an alias for `SignatureConstructionMethod`. ``` -------------------------------- ### Supported Algorithms Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Lists the digest and signature algorithms supported by XAdES, referencing ETSI TS 119 312. ```APIDOC ## Supported Algorithms ### Description Signature and digest algorithms supported by XAdES are described in ETSI TS 119 312. ### Digest Algorithms - SHA-224 (FIPS Publication 180-4) - SHA-256 (FIPS Publication 180-4) - SHA-384 (FIPS Publication 180-4) - SHA-512 (FIPS Publication 180-4) - SHA-512/256 (FIPS Publication 180-4) - SHA3-256 (FIPS Publication 202) - SHA3-384 (FIPS Publication 202) - SHA3-512 (FIPS Publication 202) ### Signature Algorithms - RSA-PKCS#1v1_5 (IETF RFC 3447) - RSA-PSS (IETF RFC 3447) - DSA (FF-DLOG DSA) (FIPS Publication 186-4, ISO/IEC 14888-3) - EC-DSA (EC-DLOG EC-DSA) (FIPS Publication 186-4) - EC-SDSA-opt (EC-DLOG EC-Schnorr) (ISO/IEC 14888-3) **Note:** Not all algorithms are fully supported. Issue 206 tracks the implementation of RFC 6931 identifiers. HMAC algorithms are not supported, and SHA1 is deprecated. ``` -------------------------------- ### Define XAdES Configuration and Data Structures Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Data classes for configuring XAdES signatures, defining policies, and specifying data object formats. ```python @dataclass(frozen=True) class XAdESSignatureConfiguration(SignatureConfiguration): """ A subclass of :class:`signxml.SignatureConfiguration`, with default overrides as described below. """ expect_references: Union[int, bool] = 3 """ By default, XAdES signatures carry 3 references (the original data reference, the KeyInfo (X.509 certificate) reference, and the signed properties reference). Signatures can carry more references if more data or extensions are present. Specify the expected number of references here. """ ``` ```python @dataclass(frozen=True) class XAdESSignaturePolicy: Identifier: str Description: str DigestMethod: DigestAlgorithm DigestValue: str ``` ```python @dataclass(frozen=True) class XAdESDataObjectFormat: Description: str = "Default XAdES payload description" MimeType: str = "text/xml" ``` -------------------------------- ### Use asn1crypto Instead of pyasn1 Source: https://xml-security.github.io/signxml/changelog.html Switches the library's dependency from `pyasn1` to `asn1crypto` to align with the `cryptography` library's requirements. ```python Use asn1crypto instead of pyasn1 to match cryptography lib (#85) ``` -------------------------------- ### POST /sign Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Signs the provided XML data and returns the root element of the resulting XML tree. ```APIDOC ## POST /sign ### Description Signs the provided data and returns the root element of the resulting XML tree. ### Method POST ### Parameters #### Request Body - **data** (String/file-like/Element) - Required - Data to sign - **key** (String/bytes/RSAPrivateKey/DSAPrivateKey/EllipticCurvePrivateKey) - Optional - Key to be used for signing - **passphrase** (bytes) - Optional - Passphrase to use to decrypt the key - **cert** (String/List) - Optional - X.509 certificate to use for signing - **reference_uri** (String/List) - Optional - Custom reference URI or list of reference URIs - **key_name** (String) - Optional - KeyName element in the KeyInfo element - **key_info** (_Element) - Optional - A custom KeyInfo element to insert in the signature - **id_attribute** (String) - Optional - Name of the attribute whose value URI refers to - **always_add_key_value** (bool) - Optional - Write the key value to the KeyInfo element even if a X509 certificate is present - **inclusive_ns_prefixes** (List) - Optional - List of XML namespace prefixes for canonicalization - **signature_properties** (_Element/List) - Optional - Elements to be included in the SignatureProperties section - **exclude_c14n_transform_element** (bool) - Optional - Exclude C14N transform element ### Response #### Success Response (200) - **result** (_Element) - The root element of the resulting XML tree. ``` -------------------------------- ### Verify SHA1-based signatures Source: https://xml-security.github.io/signxml/_modules/signxml/algorithms.html SHA1 algorithms are disabled by default due to security concerns. Use this configuration to explicitly enable them for legacy support. ```python XMLVerifier().verify( expect_config=SignatureConfiguration( signature_methods=..., digest_algorithms=... ) ) ``` -------------------------------- ### Allow Combination of X509Data and KeyValue Source: https://xml-security.github.io/signxml/changelog.html Permits the combination of `X509Data` and `KeyValue` when they represent the same public key, addressing specific XML signature scenarios. ```python Allow the combination of X509Data and KeyValue when they represent the same public key (#169) ``` -------------------------------- ### Handle XML Namespace Context Source: https://xml-security.github.io/signxml/_modules/signxml/signer.html Internal logic to determine the QName for an element, ensuring it inherits from the nsmap context to prevent spurious xmlns declarations. ```python if None in self.namespaces and self.namespaces[None] == namespaces.ds: # type:ignore[index] return QName(None, tag) return ds_tag(tag) ``` -------------------------------- ### signxml Core API Source: https://xml-security.github.io/signxml/_sources/index.rst.txt Documentation for the core signxml module, covering its main functionalities for signing XML documents. ```APIDOC ## signxml Core API ### Description This section details the core functionalities provided by the `signxml` module for creating and manipulating XML digital signatures. ### Members - `signxml` module members are documented here, excluding `XMLSignatureProcessor`. ``` -------------------------------- ### Verify Signature with Public Key Source: https://xml-security.github.io/signxml/_modules/signxml/verifier.html Verifies an XML signature using a public key, either provided directly or derived from an X.509 certificate. ```python else: if key_value is None and der_encoded_key_value is None: raise InvalidInput("Expected to find either KeyValue or X509Data XML element in KeyInfo") verified_signed_info_c14n, key_used = self._verify_signature_with_pubkey( signed_info_c14n=signed_info_c14n, raw_signature=raw_signature, key_value=key_value, der_encoded_key_value=der_encoded_key_value, signature_alg=signature_alg, ) ``` -------------------------------- ### Map digest algorithms to implementations Source: https://xml-security.github.io/signxml/_modules/signxml/algorithms.html Dictionary mapping digest algorithms and signature methods to their corresponding cryptographic hash implementations. ```python digest_algorithm_implementations: Dict[Union[DigestAlgorithm, SignatureMethod], Type[hashes.HashAlgorithm]] = { DigestAlgorithm.SHA1: hashes.SHA1, DigestAlgorithm.SHA224: hashes.SHA224, DigestAlgorithm.SHA384: hashes.SHA384, DigestAlgorithm.SHA256: hashes.SHA256, DigestAlgorithm.SHA512: hashes.SHA512, DigestAlgorithm.SHA3_224: hashes.SHA3_224, DigestAlgorithm.SHA3_256: hashes.SHA3_256, DigestAlgorithm.SHA3_384: hashes.SHA3_384, DigestAlgorithm.SHA3_512: hashes.SHA3_512, SignatureMethod.DSA_SHA1: hashes.SHA1, ``` -------------------------------- ### Add Signature Production Place Source: https://xml-security.github.io/signxml/_modules/signxml/xades/xades.html Placeholder method for adding SignatureProductionPlace or SignatureProductionPlaceV2 elements. ```python def add_signature_production_place(self, signed_signature_properties, sig_root, signing_settings: SigningSettings): # SignatureProductionPlace or SignatureProductionPlaceV2 pass ``` -------------------------------- ### Define SignXML Exception Classes Source: https://xml-security.github.io/signxml/_modules/signxml/exceptions.html Custom exception hierarchy for handling XML signature validation and input errors. ```python """ SignXML exception types. """ import cryptography.exceptions class SignXMLException(Exception): pass [docs] class InvalidSignature(cryptography.exceptions.InvalidSignature, SignXMLException): """ Raised when signature validation fails. """ [docs] class InvalidDigest(InvalidSignature): """ Raised when digest validation fails (causing the signature to be untrusted). """ [docs] class InvalidCertificate(InvalidSignature): """ Raised when certificate validation fails. """ [docs] class InvalidInput(ValueError, SignXMLException): pass class RedundantCert(SignXMLException): pass ``` -------------------------------- ### Key Value Matching Source: https://xml-security.github.io/signxml/_modules/signxml/verifier.html Compares public key information from XML KeyValue elements with provided public keys. ```APIDOC ## Internal Method: _check_key_value_matches_cert_public_key ### Description Checks if the public key information within an XML KeyValue element matches the provided public key object for specific signature algorithms (RSA, DSA, ECDSA). ### Method Internal ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **key_value** (XML Element) - The XML element containing the KeyValue. - **public_key** (object) - The public key object (e.g., RSAPublicKey, DSAPublicKey, EllipticCurvePublicKey). - **signature_alg** (SignatureMethod) - The signature algorithm object. ### Response - **boolean** - True if the keys match, False otherwise. ### Error Response - **NotImplementedError** - Raised if the signature algorithm is not supported. ``` ```APIDOC ## Internal Method: _check_der_key_value_matches_cert_public_key ### Description Checks if a DER-encoded public key from an XML element matches the provided public key object, specifically for ECDSA algorithms. ### Method Internal ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **der_encoded_key_value** (XML Element) - The XML element containing the DER-encoded public key. - **public_key** (object) - The public key object (e.g., EllipticCurvePublicKey). - **signature_alg** (SignatureMethod) - The signature algorithm object. ### Response - **boolean** - True if the DER-encoded key matches the public key, False otherwise. ### Error Response - **TODO** - Indicates that test cases for this functionality are missing. ```