@@ -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 ):
0 commit comments