Ejemplo n.º 1
0
    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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
#!/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)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
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