def verifica_assinatura_xml(self, xml, numero_tag_assinatura=0, doctype=''): xml = self._prepara_doc_xml(xml, doctype=doctype) # # Colocamos o texto no avaliador XML # doc_xml = etree.fromstring(xml.encode('utf-8')) # # A validação deve ser feita somente pela tag assinada, assim, se a # tag assinada estiver dentro de outras, precisamos tirar ela de lá # pra poder validar corretamente a assinatura # assinatura = doc_xml.xpath('//sig:Signature', namespaces=NAMESPACES) a_verificar = assinatura[numero_tag_assinatura].getparent() verificador = signxml.XMLVerifier() # # Separa o certificado da assinatura # noh_certificado = verificador._findall(a_verificar, 'X509Certificate', anywhere=True) if not noh_certificado: raise ValueError('XML sem nó X509Certificate!') noh_certificado = noh_certificado[0] self.prepara_certificado_txt(noh_certificado.text) # # Vai levantar exceção caso a assinatura seja inválida # verificador.verify( a_verificar, ca_pem_file=os.path.join(DIRNAME, 'cadeia.pem'), x509_cert=self.cert_openssl, ) return True
def verify_xml_signature(xml_file, certificate_path): """ Verify the signature of a given xml file against a certificate :param path xml_file: path to the xml file for verification :param certificate_path: path to the certificate to be used for verification :return: bool: the success of verification """ # TODO - refactor such that this verifies for generic stuff tree = etree.parse(xml_file) root = tree.getroot() with open(certificate_path) as f: certificate = f.read() # for per_tag in root.iter('UAPermission'): # data_to_sign = per_tag try: verified_data = sx.XMLVerifier().verify(data=root, require_x509=True, x509_cert=certificate).signed_xml # The file signature is authentic return True except cryptography.exceptions.InvalidSignature: # print(verified_data) # add the type of exception return False
HAND_BUILT_PAYLOAD = False signed_object_xml = open('sample_for_crypto_experiment.xml', 'rb').read() signed_object_lxml = etree_.fromstring(signed_object_xml) pretty_print_lxml('Original XML to be signed:', signed_object_lxml) # Use "detached method": the signature lives alonside the signed object in the XML element tree. # Use c14n "exclusive canonicalization": the signature is independent of namespace inclusion/exclusion. signer = signxml.XMLSigner(method=signxml.methods.detached, c14n_algorithm='http://www.w3.org/2001/10/xml-exc-c14n#') signature_lxml = signer.sign(signed_object_lxml, key=open(CERTS_DIRECTORY + KEY_FILENAME, 'rb').read(), cert=open(CERTS_DIRECTORY + CERT_FILENAME, 'rb').read(), key_name='123') pretty_print_lxml('Signed root:', signature_lxml) signature_xml = etree_.tostring(signature_lxml) if HAND_BUILT_PAYLOAD: payload = XML_PREFIX + PAYLOAD_START_TAG + signature_xml + signed_object_xml + PAYLOAD_END_TAG else: payload = etree_.Element("{http://openadr.org/oadr-2.0b/2012/07}oadrPayload", nsmap=signed_object_lxml.nsmap) payload.append(signature_lxml) payload.append(signed_object_lxml) pretty_print_lxml('Payload:', payload) # Confirm that the signed payload can be verified. verif = signxml.XMLVerifier().verify(payload, ca_pem_file=CERTS_DIRECTORY + VTN_CA_CERT_FILENAME) verified_data_lxml = verif.signed_xml pretty_print_lxml('Verified data:', verified_data_lxml)
#!/usr/bin/env vpython3 # *-* coding: utf-8 *-* import sys from lxml import etree import signxml if sys.argv[1:]: fname = sys.argv[1] else: fname = "xml-xmlsigner-enveloped.xml" #fname = "xml-xmlsigner-enveloping.xml" #fname = 'xml-xades-bes-enveloped.xml' data = open(fname, "rb").read() signed_root = etree.fromstring(data) verified_data = ( signxml.XMLVerifier().verify(signed_root, ca_pem_file="demo2_ca.crt.pem").signed_xml ) xml = etree.tostring( verified_data, encoding="UTF-8", xml_declaration=True, standalone=False, pretty_print=True, ) # open(fname.replace('.xml', '-result.xml'), "wb").write(xml)
start_dt = datetime.datetime.now() schema = xmlschema.XMLSchema(xsd_file, locations=locations, loglevel=loglevel) elapsed_time = datetime.datetime.now() - start_dt print("Schema: OK (elapsed time: {})".format(elapsed_time)) exit_code = 0 for xml_file in args.xml_file: print("\nValidate XML file {!r} ...".format(xml_file)) start_dt = datetime.datetime.now() try: if cert is not None: signed_xml_data = lxml.etree.parse(xml_file).getroot() signxml.XMLVerifier().verify(signed_xml_data, x509_cert=cert) elapsed_time = datetime.datetime.now() - start_dt print("XML signature validation: OK (elapsed time: {})".format( elapsed_time)) start_dt = datetime.datetime.now() xml_file = signed_xml_data xmlschema.validate(xml_file, schema=schema, lazy=args.lazy) except xmlschema.XMLSchemaValidationError as err: exit_code = 1 print("XML schema validation: FAIL") if args.verbosity >= 2: print(err)
def verify_xml_signature( xml_doc: XmlElement, trusted_x509_cert: Union[crypto_utils.X509Cert, crypto_utils._X509CertOpenSsl] = None, ) -> Tuple[bytes, XmlElementTree, XmlElementTree]: """ Verify the XML signature in ``xml_doc``. .. note:: XML document with more than one signature is not supported. If the inputs are ok but the XML signature does not verify, raises :class:`XmlSignatureUnverified`. If ``trusted_x509_cert`` is None, it requires that the signature in ``xml_doc`` includes a a valid X.509 **certificate chain** that validates against the *known certificate authorities*. If ``trusted_x509_cert`` is given, it must be a **trusted** external X.509 certificate, and the verification will be of whether the XML signature in ``xml_doc`` was signed by ``trusted_x509_cert`` or not; thus **it overrides** any X.509 certificate information included in the signature. .. note:: It is strongly recommended to validate ``xml_doc`` beforehand (against the corresponding XML schema, using :func:`validate_xml_doc`). :param xml_doc: :param trusted_x509_cert: a trusted external X.509 certificate, or None :raises :class:`XmlSignatureUnverified`: signature did not verify :raises :class:`XmlSignatureInvalidCertificate`: certificate validation failed :raises :class:`XmlSignatureInvalid`: signature is invalid :raises :class:`XmlSchemaDocValidationError`: XML doc is not valid :raises :class:`ValueError`: """ if not isinstance(xml_doc, XmlElement): raise TypeError("'xml_doc' must be an XML document/element.") n_signatures = ( len(xml_doc.findall('.//ds:Signature', namespaces=XML_DSIG_NS_MAP)) + len(xml_doc.findall('.//dsig11:Signature', namespaces=XML_DSIG_NS_MAP)) + len(xml_doc.findall('.//dsig2:Signature', namespaces=XML_DSIG_NS_MAP))) if n_signatures > 1: raise NotImplementedError( "XML document with more than one signature is not supported.") xml_verifier = signxml.XMLVerifier() if isinstance(trusted_x509_cert, crypto_utils._X509CertOpenSsl): trusted_x509_cert_open_ssl = trusted_x509_cert elif isinstance(trusted_x509_cert, crypto_utils.X509Cert): trusted_x509_cert_open_ssl = crypto_utils._X509CertOpenSsl.from_cryptography( trusted_x509_cert) elif trusted_x509_cert is None: trusted_x509_cert_open_ssl = None else: # A 'crypto_utils._X509CertOpenSsl' is ok but we prefer 'crypto_utils.X509Cert'. raise TypeError( "'trusted_x509_cert' must be a 'crypto_utils.X509Cert' instance, or None." ) # warning: performance issue. # note: 'signxml.XMLVerifier.verify()' calls 'signxml.util.XMLProcessor.get_root()', # which converts the data to string, and then reparses it using the same function we use # in 'parse_untrusted_xml()' ('defusedxml.lxml.fromstring'), but without all the precautions # we have there. See: # https://github.com/XML-Security/signxml/blob/v2.6.0/signxml/util/__init__.py#L141-L151 # Considering that, we'd rather write to bytes ourselves and control the process. f = io.BytesIO() write_xml_doc(xml_doc, f) tmp_bytes = f.getvalue() try: # note: by passing 'x509_cert' we override any X.509 certificate information supplied # by the signature itself. # note: when an X509Data element is present in the signature and used for verification, but # a KeyValue element is also present, there is an ambiguity and a security hazard because # the public key used to sign the document is already encoded in the certificate (which is # in X509Data), so the verifier must either ignore KeyValue or ensure that it matches what # is in the certificate. SignXML does not perform that validation and throws an # 'InvalidInput' error instead. # # SII's schema for XML signatures requires both elements to be present, which forces us to # enable 'ignore_ambiguous_key_info' to bypass the error and validate the signature using # X509Data only. # # Source: # https://github.com/XML-Security/signxml/commit/ef15da8dbb904f1dedfdd210ae3e0df5da535612 result: signxml.VerifyResult = xml_verifier.verify( data=tmp_bytes, require_x509=True, x509_cert=trusted_x509_cert_open_ssl, ignore_ambiguous_key_info=True, ) except signxml.exceptions.InvalidDigest as exc: # warning: catch before 'InvalidSignature' (it is the parent of 'InvalidDigest'). raise XmlSignatureUnverified(str(exc)) from exc except signxml.exceptions.InvalidCertificate as exc: # warning: catch before 'InvalidSignature' (it is the parent of 'InvalidCertificate'). raise XmlSignatureInvalidCertificate(str(exc)) from exc except signxml.exceptions.InvalidSignature as exc: # XML signature is invalid, for any reason. raise XmlSignatureInvalid(str(exc)) from exc except signxml.exceptions.InvalidInput as exc: raise ValueError("Invalid input.", str(exc)) from exc except lxml.etree.DocumentInvalid as exc: # Simplest and safest way to get the error message (see 'validate_xml_doc()'). # Error example: # "Element '{http://www.w3.org/2000/09/xmldsig#}X509Certificate': '\nabc\n' is not a # valid value of the atomic type 'xs:base64Binary'., line 30" validation_error_msg = str(exc) raise XmlSchemaDocValidationError(validation_error_msg) from exc return result.signed_data, result.signed_xml, result.signature_xml