Skip to content

Commit 18efa28

Browse files
committed
Begin X509IssuerSerial/X509Digest support
1 parent a191049 commit 18efa28

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

signxml/__init__.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -661,8 +661,8 @@ def _apply_transforms(self, payload, transforms_node, signature, c14n_algorithm)
661661
return payload
662662

663663
def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None, ca_pem_file=None, ca_path=None,
664-
hmac_key=None, validate_schema=True, parser=None, uri_resolver=None, id_attribute=None,
665-
expect_references=1, ignore_ambiguous_key_info=False):
664+
hmac_key=None, validate_schema=True, parser=None, uri_resolver=None, cert_resolver=None,
665+
id_attribute=None, expect_references=1, ignore_ambiguous_key_info=False):
666666
"""
667667
Verify the XML signature supplied in the data and return the XML node signed by the signature, or raise an
668668
exception if the signature is not valid. By default, this requires the signature to be generated using a valid
@@ -724,8 +724,16 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
724724
Custom XML parser instance to use when parsing **data**. The default parser arguments used by SignXML are:
725725
``resolve_entities=False``. See https://lxml.de/FAQ.html#how-do-i-use-lxml-safely-as-a-web-service-endpoint.
726726
:type parser: :py:class:`lxml.etree.XMLParser` compatible parser
727-
:param uri_resolver: Function to use to resolve reference URIs that don't start with "#".
727+
:param uri_resolver:
728+
Function to use to resolve reference URIs that don't start with "#". The function is called with a single
729+
string argument containing the URI to be resolved, and is expected to return a lxml.etree node or string.
728730
:type uri_resolver: callable
731+
:param cert_resolver:
732+
Function to use to resolve X.509 certificates when X509IssuerSerial and X509Digest references are found in
733+
the signature. The function is called with the keyword arguments ``x509_issuer_name``,
734+
``x509_serial_number`` and ``x509_digest``, and is expected to return an iterable of one or more
735+
strings containing PEM-formatted certificates.
736+
:type cert_resolver: callable
729737
:param id_attribute:
730738
Name of the attribute whose value ``URI`` refers to. By default, SignXML will search for "Id", then "ID".
731739
:type id_attribute: string
@@ -791,10 +799,18 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
791799
if x509_data is None:
792800
raise InvalidInput("Expected a X.509 certificate based signature")
793801
certs = [cert.text for cert in self._findall(x509_data, "X509Certificate")]
794-
if not certs:
795-
msg = "Expected to find an X509Certificate element in the signature"
796-
msg += " (X509SubjectName, X509SKI are not supported)"
797-
raise InvalidInput(msg)
802+
if len(certs) == 0:
803+
x509_iss = x509_data.find("ds:X509IssuerSerial/ds:X509IssuerName", namespaces=namespaces)
804+
x509_sn = x509_data.find("ds:X509IssuerSerial/ds:X509SerialNumber", namespaces=namespaces)
805+
x509_digest = x509_data.find("dsig11:X509Digest", namespaces=namespaces)
806+
if cert_resolver is not None and (x509_iss or x509_sn or x509_digest):
807+
certs = cert_resolver(x509_issuer_name=x509_iss.text if x509_iss is not None else None,
808+
x509_serial_number=x509_sn.text if x509_sn is not None else None,
809+
x509_digest=x509_digest.text if x509_digest is not None else None)
810+
else:
811+
msg = "Expected to find an X509Certificate element in the signature"
812+
msg += " (X509SubjectName, X509SKI are not supported)"
813+
raise InvalidInput(msg)
798814
cert_chain = [load_certificate(FILETYPE_PEM, add_pem_header(cert)) for cert in certs]
799815
signing_cert = verify_x509_cert_chain(cert_chain, ca_pem_file=ca_pem_file, ca_path=ca_path)
800816
elif isinstance(self.x509_cert, X509):

test/test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def get_x509_cert(signature_file):
199199
XMLVerifier().verify(sig, require_x509=False, hmac_key="testkey", validate_schema=True)
200200
decoded_sig = sig.decode("utf-8")
201201
except Exception as e:
202-
if "keyinforeference" in signature_file or "x509digest" in signature_file:
202+
if "keyinforeference" in signature_file:
203203
print("Unsupported test case:", type(e), e)
204204
else:
205205
raise
@@ -260,7 +260,7 @@ def get_ca_pem_file(signature_file):
260260
raise BaseException("Expected an exception to occur")
261261
except Exception as e:
262262
unsupported_cases = ("xpath-transform", "xslt-transform", "xpointer",
263-
"x509-data-issuer-serial", "x509-data-ski", "x509-data-subject-name",
263+
"x509-data-ski", "x509-data-subject-name",
264264
"x509data", "signature-x509-ski", "signature-x509-is")
265265
bad_interop_cases = ("signature-big", "enveloping-dsa-x509chain",
266266
"enveloping-sha512-hmac-sha512", "enveloping-sha512-rsa-sha512",

0 commit comments

Comments
 (0)