예제 #1
0
    def test_sign_case2(self):
        """Should sign a dynamicaly constructed template file using a key from a PEM file."""
        root = parse_xml("data/sign2-in.xml")
        sign = xmlsig.template.create(
            c14n_method=xmlsig.constants.TransformExclC14N,
            sign_method=xmlsig.constants.TransformRsaSha1,
            ns=None)
        self.assertIsNotNone(sign)
        root.append(sign)
        ref = xmlsig.template.add_reference(sign,
                                            xmlsig.constants.TransformSha1)
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
        ki = xmlsig.template.ensure_key_info(sign)
        xmlsig.template.add_key_name(ki)
        xmlsig.template.add_key_value(ki)

        ctx = xmlsig.SignatureContext()
        with open(path.join(BASE_DIR, "data/rsakey.pem"), "rb") as key_file:
            ctx.private_key = serialization.load_pem_private_key(
                key_file.read(), password=None, backend=default_backend())
        ctx.key_name = 'rsakey.pem'
        self.assertEqual("rsakey.pem", ctx.key_name)
        ctx.sign(sign)
        with open(path.join(BASE_DIR, "data/rsacert.pem"), "rb") as cert_file:
            x509 = load_pem_x509_certificate(cert_file.read(),
                                             default_backend())
        ctx.verify(sign)
        ctx.public_key = x509.public_key()
        ctx.verify(sign)
        compare("data/sign2-out.xml", root)
예제 #2
0
def get_xml_soap_with_signature(xml_soap_without_signature, Id,
                                certificate_file, certificate_password):
    wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    X509v3 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
    parser = etree.XMLParser(remove_comments=True)
    root = etree.fromstring(xml_soap_without_signature, parser=parser)
    signature_id = "{}".format(Id)
    signature = xmlsig.template.create(
        xmlsig.constants.TransformExclC14N,
        xmlsig.constants.TransformRsaSha256,  # solo me ha funcionado con esta
        "SIG-" + signature_id)
    ref = xmlsig.template.add_reference(signature,
                                        xmlsig.constants.TransformSha256,
                                        uri="#id-" + signature_id)
    xmlsig.template.add_transform(ref, xmlsig.constants.TransformExclC14N)
    ki = xmlsig.template.ensure_key_info(signature, name="KI-" + signature_id)
    ctx = xmlsig.SignatureContext()
    ctx.load_pkcs12(get_pkcs12(certificate_file, certificate_password))

    for element in root.iter("{%s}Security" % wsse):
        element.append(signature)

    ki_str = etree.SubElement(ki, "{%s}SecurityTokenReference" % wsse)
    ki_str.attrib["{%s}Id" % wsu] = "STR-" + signature_id
    ki_str_reference = etree.SubElement(ki_str, "{%s}Reference" % wsse)
    ki_str_reference.attrib['URI'] = "#X509-" + signature_id
    ki_str_reference.attrib['ValueType'] = X509v3
    ctx.sign(signature)
    ctx.verify(signature)

    return root
예제 #3
0
def verify_envelope(envelope, cert):
    """Verify WS-Security signature on given SOAP envelope with given cert.

    Expects a document like that found in the sample XML in the ``sign()``
    docstring.

    Raise SignatureVerificationFailed on failure, silent on success.

    """
    soap_env = detect_soap_env(envelope)

    header = envelope.find(QName(soap_env, "Header"))
    if header is None:
        raise SignatureVerificationFailed()

    security = header.find(QName(ns.WSSE, "Security"))
    signature = security.find(QName(ns.DS, "Signature"))
    key_text = security.find(QName(ns.WSSE, "BinarySecurityToken")).text
    key = x509.load_der_x509_certificate(base64.b64decode(key_text))
    ctx = xmlsig.SignatureContext()
    ctx.public_key = key.public_key()
    try:
        ctx.verify(signature)
    except Exception:
        # Sadly xmlsec gives us no details about the reason for the failure, so
        # we have nothing to pass on except that verification failed.
        raise SignatureVerificationFailed() from None
예제 #4
0
    def test_sign_case4(self):
        """Should sign a file using a dynamically created template, key from PEM and an X509 cert with custom ns."""

        root = parse_xml("data/sign4-in.xml")
        elem_id = root.get('ID', None)
        if elem_id:
            elem_id = '#' + elem_id
        sign = xmlsig.template.create(
            c14n_method=xmlsig.constants.TransformExclC14N,
            sign_method=xmlsig.constants.TransformRsaSha1,
            ns="ds")
        self.assertIsNotNone(sign)
        root.append(sign)
        ref = xmlsig.template.add_reference(sign,
                                            xmlsig.constants.TransformSha1,
                                            uri=elem_id)
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformExclC14N)
        ki = xmlsig.template.ensure_key_info(sign)
        xmlsig.template.x509_data_add_certificate(
            xmlsig.template.add_x509_data(ki))
        ctx = xmlsig.SignatureContext()
        with open(path.join(BASE_DIR, "data/rsakey.pem"), "rb") as key_file:
            ctx.private_key = serialization.load_pem_private_key(
                key_file.read(), password=None, backend=default_backend())
        ctx.key_name = 'rsakey.pem'
        self.assertEqual("rsakey.pem", ctx.key_name)
        with open(path.join(BASE_DIR, "data/rsacert.pem"), "rb") as cert_file:
            ctx.x509 = load_pem_x509_certificate(cert_file.read(),
                                                 default_backend())
        ctx.sign(sign)
        ctx.verify(sign)
        compare("data/sign4-out.xml", root)
예제 #5
0
    def test_signature(self):
        self._activate_certificate(self.certificate_password)
        self.move.action_post()
        self.move.name = "2999/99999"
        self.main_company.partner_id.country_id = self.env.ref("base.es")
        self.wizard.with_context(
            active_ids=self.move.ids, active_model="account.move"
        ).create_facturae_file()
        generated_facturae = etree.fromstring(base64.b64decode(self.wizard.facturae))
        ns = "http://www.w3.org/2000/09/xmldsig#"
        self.assertEqual(
            len(generated_facturae.xpath("//ds:Signature", namespaces={"ds": ns})), 1
        )

        node = generated_facturae.find(".//ds:Signature", {"ds": ns})
        ctx = xmlsig.SignatureContext()
        verification_error = False
        error_message = ""
        try:
            ctx.verify(node)
        except Exception as e:
            verification_error = True
            error_message = str(e)
        self.assertEqual(
            verification_error,
            False,
            "Error found during verification of the signature of "
            + "the move: %s" % error_message,
        )
예제 #6
0
    def test_hmac(self):
        template = parse_xml('data/sign-hmac-in.xml')

        # Create a signature template for RSA-SHA1 enveloped signature.
        sign = xmlsig.template.create(
            c14n_method=xmlsig.constants.TransformExclC14N,
            sign_method=xmlsig.constants.TransformHmacSha1,
            ns="ds")

        assert sign is not None

        # Add the <ds:Signature/> node to the document.
        template.append(sign)

        # Add the <ds:Reference/> node to the signature template.
        ref = xmlsig.template.add_reference(sign,
                                            xmlsig.constants.TransformSha1)
        # Add the enveloped transform descriptor.
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)

        ref_obj = xmlsig.template.add_reference(sign,
                                                xmlsig.constants.TransformSha1,
                                                uri="#R1")
        xmlsig.template.add_transform(ref_obj,
                                      xmlsig.constants.TransformBase64)
        obj = etree.SubElement(sign,
                               etree.QName(xmlsig.constants.DSigNs, 'Object'))
        obj.set("Id", "R1")
        obj.text = base64.b64encode(b"Some Text")
        ctx = xmlsig.SignatureContext()
        ctx.private_key = b"secret"

        ctx.sign(sign)
        ctx.verify(sign)
        compare('data/sign-hmac-out.xml', template)
예제 #7
0
    def test_fail_signature(self):
        """Should sign a dynamicaly constructed template file using a key from a PEM file."""
        root = parse_xml("data/sign-fail_signature.xml")
        sign = root.xpath('//ds:Signature',
                          namespaces={'ds': xmlsig.constants.DSigNs})[0]

        ctx = xmlsig.SignatureContext()
        with self.assertRaises(Exception):
            ctx.verify(sign)
예제 #8
0
    def parse_facturae_invoice(self, xml_root, xsd_file):
        facturae_schema = etree.XMLSchema(
            etree.parse(
                tools.file_open(
                    xsd_file,
                    subdir="addons/account_invoice_import_facturae/data")))
        facturae_schema.assertValid(xml_root)
        sign = xml_root.find('ds:Signature',
                             namespaces={'ds': xmlsig.constants.DSigNs})
        if sign is not None:
            xmlsig.SignatureContext().verify(sign)
        modality = xml_root.find('FileHeader/Modality').text
        if modality == 'L':
            raise ValidationError(_('System does not allow lots'))
        supplier_dict = self.facturae_parse_partner(
            xml_root, xml_root.find('Parties/SellerParty'))
        invoice = xml_root.find('Invoices/Invoice')

        inv_number_xpath = invoice.find('InvoiceHeader/InvoiceNumber')
        inv_type = 'in_invoice'
        inv_class = invoice.find('InvoiceHeader/InvoiceClass')
        if inv_class is not None and inv_class.text not in ['OO', 'OC']:
            inv_type = 'in_refund'
        date_dt = datetime.strptime(
            invoice.find('InvoiceIssueData/IssueDate').text, '%Y-%M-%d')
        date_start = False
        date_end = False
        amount_total = float(invoice.find('InvoiceTotals/InvoiceTotal').text)
        amount_untaxed = float(
            invoice.find('InvoiceTotals/TotalGrossAmountBeforeTaxes').text)
        res_lines = [
            self.facturae_parse_line(xml_root, invoice, line)
            for line in invoice.find('Items').findall('InvoiceLine')
        ]
        attachments = {}
        res = {
            'type': inv_type,
            'partner': supplier_dict,
            'invoice_number': inv_number_xpath.text,
            'date': fields.Date.to_string(date_dt),
            'date_due': False,
            'date_start': date_start,
            'date_end': date_end,
            'currency': {
                'iso':
                invoice.find('InvoiceIssueData/InvoiceCurrencyCode').text
            },
            'amount_total': amount_total,
            'amount_untaxed': amount_untaxed,
            'iban': False,
            'bic': False,
            'lines': res_lines,
            'attachments': attachments,
        }
        logger.info('Result of Facturae XML parsing: %s', res)
        return res
예제 #9
0
    def tes_sign_case1(self):
        """Should sign a pre-constructed template file using a key from a PEM file."""
        root = parse_xml("data/sign1-in.xml")
        sign = root.xpath('//ds:Signature',
                          namespaces={'ds': xmlsig.constants.DSigNs})[0]
        self.assertIsNotNone(sign)

        ctx = xmlsig.SignatureContext()
        with open(path.join(BASE_DIR, "data/rsakey.pem"), "rb") as key_file:
            ctx.private_key = serialization.load_pem_private_key(
                key_file.read(), password=None, backend=default_backend())
        ctx.key_name = 'rsakey.pem'
        self.assertEqual("rsakey.pem", ctx.key_name)

        ctx.sign(sign)
        ctx.verify(sign)
        compare('sign1-out.xml', root)
예제 #10
0
    def test_signature(self):
        pkcs12 = crypto.PKCS12()
        pkey = crypto.PKey()
        pkey.generate_key(crypto.TYPE_RSA, 512)
        x509 = crypto.X509()
        x509.set_pubkey(pkey)
        x509.set_serial_number(0)
        x509.get_subject().CN = "me"
        x509.set_issuer(x509.get_subject())
        x509.gmtime_adj_notBefore(0)
        x509.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
        x509.sign(pkey, 'md5')
        pkcs12.set_privatekey(pkey)
        pkcs12.set_certificate(x509)
        self.main_company.facturae_cert = base64.b64encode(
            pkcs12.export(passphrase='password'))
        self.main_company.facturae_cert_password = '******'
        self.main_company.partner_id.country_id = self.ref('base.es')
        self.wizard.with_context(
            active_ids=self.invoice.ids,
            active_model='account.invoice').create_facturae_file()
        generated_facturae = etree.fromstring(
            base64.b64decode(self.wizard.facturae))
        ns = 'http://www.w3.org/2000/09/xmldsig#'
        self.assertEqual(
            len(
                generated_facturae.xpath('//ds:Signature',
                                         namespaces={'ds': ns})), 1)

        node = generated_facturae.find(".//ds:Signature", {'ds': ns})
        ctx = xmlsig.SignatureContext()
        certificate = crypto.load_pkcs12(
            base64.b64decode(self.main_company.facturae_cert), 'password')
        certificate.set_ca_certificates(None)
        verification_error = False
        error_message = ''
        try:
            ctx.verify(node)
        except Exception as e:
            verification_error = True
            error_message = e.message
            pass
        self.assertEqual(
            verification_error, False,
            'Error found during verification of the signature of ' +
            'the invoice: %s' % error_message)
예제 #11
0
    def test_sign_generated_template_pem_with_x509(self):
        """
        Should sign a file using a dynamicaly created template, key from PEM
        file and an X509 certificate.
        """

        # Load document file.
        template = parse_xml('data/sign-doc.xml')

        # Create a signature template for RSA-SHA1 enveloped signature.
        sign = xmlsig.template.create(
            c14n_method=xmlsig.constants.TransformExclC14N,
            sign_method=xmlsig.constants.TransformRsaSha1,
            ns=None)

        assert sign is not None

        # Add the <ds:Signature/> node to the document.
        template.append(sign)

        # Add the <ds:Reference/> node to the signature template.
        ref = xmlsig.template.add_reference(sign,
                                            xmlsig.constants.TransformSha1)

        # Add the enveloped transform descriptor.
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)

        # Add the <ds:KeyInfo/> and <ds:KeyName/> nodes.
        key_info = xmlsig.template.ensure_key_info(sign)
        x509_data = xmlsig.template.add_x509_data(key_info)
        xmlsig.template.x509_data_add_certificate(x509_data)
        # Create a digital signature context (no key manager is needed).
        # Load private key (assuming that there is no password).
        # Set the key on the context.
        ctx = xmlsig.SignatureContext()

        with open(path.join(BASE_DIR, "data/keyStore.p12"), "rb") as key_file:
            ctx.load_pkcs12(crypto.load_pkcs12(key_file.read()))
        # Sign the template.
        ctx.sign(sign)
        ctx.verify(sign)
        # Assert the contents of the XML document against the expected result.
        compare('data/sign-res.xml', template)
예제 #12
0
 def test_sign_case5(self):
     """Should sign a file using a dynamicaly created template, key from 
     PEM file and an X509 certificate."""
     root = parse_xml("data/sign5-in.xml")
     sign = xmlsig.template.create(
         c14n_method=xmlsig.constants.TransformExclC14N,
         sign_method=xmlsig.constants.TransformRsaSha256,
         ns=None,
         name="S1")
     self.assertIsNotNone(sign)
     root.append(sign)
     ref = xmlsig.template.add_reference(sign,
                                         xmlsig.constants.TransformSha1,
                                         name="R1",
                                         uri_type="Type")
     xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
     xmlsig.template.ensure_key_info(sign, name="KI1")
     ki = xmlsig.template.ensure_key_info(sign)
     x509 = xmlsig.template.add_x509_data(ki)
     xmlsig.template.x509_data_add_subject_name(x509)
     xmlsig.template.x509_data_add_certificate(x509)
     xmlsig.template.x509_data_add_ski(x509)
     x509_issuer_serial = xmlsig.template.x509_data_add_issuer_serial(x509)
     xmlsig.template.x509_issuer_serial_add_issuer_name(x509_issuer_serial)
     xmlsig.template.x509_issuer_serial_add_serial_number(
         x509_issuer_serial)
     xmlsig.template.add_key_value(ki)
     xmlsig.template.add_key_name(ki, 'rsakey.pem')
     ctx = xmlsig.SignatureContext()
     with open(path.join(BASE_DIR, "data/rsakey.pem"), "rb") as key_file:
         ctx.private_key = serialization.load_pem_private_key(
             key_file.read(), password=None, backend=default_backend())
     with open(path.join(BASE_DIR, "data/rsacert.pem"), "rb") as cert_file:
         ctx.x509 = load_pem_x509_certificate(cert_file.read(),
                                              default_backend())
     ctx.sign(sign)
     ctx.verify(sign)
     compare("data/sign5-out.xml", root)
예제 #13
0
    def sign(root, certificate, tax_agency):
        """
        Sign XML with PKCS #12
        :author: Victor Laskurain <*****@*****.**>
        :return: SignatureValue
        """
        def create_node_tree(root_node, elem_list):
            """Convierte una lista en XML.

            Cada elemento de la lista se interpretará de la siguiente manera:

            Si es un string se añadirá al texto suelto del nodo root.
            Si es una tupla/lista t se interpretará como sigue:
                t[0]  es el nombre del elemento a crear. Puede tener prefijo de espacio
                de nombres.
                t[1]  es la lista de atributos donde las posiciones pares son claves y
                los impares valores.
                t[2:] son los subelementos que se interpretan recursivamente
            """
            for elem_def in elem_list:
                if isinstance(elem_def, str):
                    root_node.text = (root_node.text or "") + elem_def
                else:
                    ns = ""
                    elemname = elem_def[0]
                    attrs = elem_def[1]
                    children = elem_def[2:]
                    if ":" in elemname:
                        ns, elemname = elemname.split(":")
                        ns = root_node.nsmap[ns]
                    node = xmlsig.utils.create_node(elemname, root_node, ns)
                    for attr_name, attr_value in zip(attrs[::2], attrs[1::2]):
                        node.set(attr_name, attr_value)
                    create_node_tree(node, children)

        doc_id = "id-" + str(uuid4())
        signature_id = "sig-" + doc_id
        kinfo_id = "ki-" + doc_id
        sp_id = "sp-" + doc_id
        signature = xmlsig.template.create(
            xmlsig.constants.TransformInclC14N,
            xmlsig.constants.TransformRsaSha256,
            signature_id,
        )
        ref = xmlsig.template.add_reference(signature,
                                            xmlsig.constants.TransformSha256,
                                            uri="")
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
        xmlsig.template.add_reference(signature,
                                      xmlsig.constants.TransformSha256,
                                      uri="#" + kinfo_id)
        xmlsig.template.add_reference(
            signature,
            xmlsig.constants.TransformSha256,
            uri="#" + sp_id,
            uri_type="http://uri.etsi.org/01903#SignedProperties",
        )
        ki = xmlsig.template.ensure_key_info(signature, name=kinfo_id)
        data = xmlsig.template.add_x509_data(ki)
        xmlsig.template.x509_data_add_certificate(data)
        xmlsig.template.add_key_value(ki)
        ctx = xmlsig.SignatureContext()
        ctx.x509 = certificate[1]
        ctx.public_key = ctx.x509.public_key()
        ctx.private_key = certificate[0]
        dslist = (
            "ds:Object",
            (),
            (
                "etsi:QualifyingProperties",
                ("Target", "#" + signature_id),
                (
                    "etsi:SignedProperties",
                    ("Id", sp_id),
                    (
                        "etsi:SignedSignatureProperties",
                        (),
                        ("etsi:SigningTime", (), datetime.now().isoformat()),
                        (
                            "etsi:SigningCertificateV2",
                            (),
                            (
                                "etsi:Cert",
                                (),
                                (
                                    "etsi:CertDigest",
                                    (),
                                    (
                                        "ds:DigestMethod",
                                        (
                                            "Algorithm",
                                            "http://www.w3.org/2001/04/xmlenc#sha256",
                                        ),
                                    ),
                                    (
                                        "ds:DigestValue",
                                        (),
                                        b64encode(
                                            ctx.x509.fingerprint(
                                                hashes.SHA256())).decode(),
                                    ),
                                ),
                            ),
                        ),
                        (
                            "etsi:SignaturePolicyIdentifier",
                            (),
                            (
                                "etsi:SignaturePolicyId",
                                (),
                                (
                                    "etsi:SigPolicyId",
                                    (),
                                    ("etsi:Identifier", (),
                                     tax_agency.sign_file_url),
                                    (
                                        "etsi:Description",
                                        (),
                                    ),
                                ),
                                (
                                    "etsi:SigPolicyHash",
                                    (),
                                    (
                                        "ds:DigestMethod",
                                        (
                                            "Algorithm",
                                            "http://www.w3.org/2001/04/xmlenc#sha256",
                                        ),
                                    ),
                                    ("ds:DigestValue", (),
                                     tax_agency.sign_file_hash),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        )
        root.append(signature)
        create_node_tree(signature, [dslist])
        ctx.sign(signature)
        signature_value = signature.find(
            "ds:SignatureValue", namespaces=xmlsig.constants.NS_MAP).text
        # RFC2045 - Base64 Content-Transfer-Encoding (page 25)
        # Any characters outside of the base64 alphabet are to be ignored in
        # base64-encoded data.
        return signature_value.replace("\n", "")
예제 #14
0
 def _sign_file(cert, password, request):
     min = 1
     max = 99999
     signature_id = 'Signature%05d' % random.randint(min, max)
     signed_properties_id = signature_id + '-SignedProperties%05d' \
                                           % random.randint(min, max)
     key_info_id = 'KeyInfo%05d' % random.randint(min, max)
     reference_id = 'Reference%05d' % random.randint(min, max)
     object_id = 'Object%05d' % random.randint(min, max)
     etsi = 'http://uri.etsi.org/01903/v1.3.2#'
     sig_policy_identifier = 'http://www.facturae.es/' \
                             'politica_de_firma_formato_facturae/' \
                             'politica_de_firma_formato_facturae_v3_1' \
                             '.pdf'
     sig_policy_hash_value = 'Ohixl6upD6av8N7pEvDABhEL6hM='
     root = etree.fromstring(request)
     sign = xmlsig.template.create(
         c14n_method=xmlsig.constants.TransformInclC14N,
         sign_method=xmlsig.constants.TransformRsaSha1,
         name=signature_id,
         ns="ds")
     key_info = xmlsig.template.ensure_key_info(sign, name=key_info_id)
     x509_data = xmlsig.template.add_x509_data(key_info)
     xmlsig.template.x509_data_add_certificate(x509_data)
     xmlsig.template.add_key_value(key_info)
     certificate = crypto.load_pkcs12(base64.b64decode(cert), password)
     xmlsig.template.add_reference(
         sign,
         xmlsig.constants.TransformSha1,
         uri='#' + signed_properties_id,
         uri_type='http://uri.etsi.org/01903#SignedProperties')
     xmlsig.template.add_reference(sign,
                                   xmlsig.constants.TransformSha1,
                                   uri='#' + key_info_id)
     ref = xmlsig.template.add_reference(sign,
                                         xmlsig.constants.TransformSha1,
                                         name=reference_id)
     xmlsig.template.add_transform(ref,
                                   xmlsig.constants.TransformEnveloped)
     object_node = etree.SubElement(
         sign,
         etree.QName(xmlsig.constants.DSigNs, 'Object'),
         nsmap={'etsi': etsi},
         attrib={xmlsig.constants.ID_ATTR: object_id})
     qualifying_properties = etree.SubElement(
         object_node,
         etree.QName(etsi, 'QualifyingProperties'),
         attrib={'Target': '#' + signature_id})
     signed_properties = etree.SubElement(
         qualifying_properties,
         etree.QName(etsi, 'SignedProperties'),
         attrib={xmlsig.constants.ID_ATTR: signed_properties_id})
     signed_signature_properties = etree.SubElement(
         signed_properties,
         etree.QName(etsi, 'SignedSignatureProperties'))
     now = datetime.now().replace(microsecond=0, tzinfo=pytz.utc)
     etree.SubElement(signed_signature_properties,
                      etree.QName(
                          etsi, 'SigningTime')).text = now.isoformat()
     signing_certificate = etree.SubElement(
         signed_signature_properties,
         etree.QName(etsi, 'SigningCertificate'))
     signing_certificate_cert = etree.SubElement(
         signing_certificate, etree.QName(etsi, 'Cert'))
     cert_digest = etree.SubElement(signing_certificate_cert,
                                    etree.QName(etsi, 'CertDigest'))
     etree.SubElement(
         cert_digest,
         etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
         attrib={'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'})
     hash_cert = hashlib.sha1(
         crypto.dump_certificate(crypto.FILETYPE_ASN1,
                                 certificate.get_certificate()))
     etree.SubElement(
         cert_digest, etree.QName(
             xmlsig.constants.DSigNs,
             'DigestValue')).text = base64.b64encode(hash_cert.digest())
     issuer_serial = etree.SubElement(signing_certificate_cert,
                                      etree.QName(etsi, 'IssuerSerial'))
     etree.SubElement(
         issuer_serial,
         etree.QName(xmlsig.constants.DSigNs, 'X509IssuerName')
     ).text = xmlsig.utils.get_rdns_name(
         certificate.get_certificate().to_cryptography().issuer.rdns)
     etree.SubElement(
         issuer_serial,
         etree.QName(
             xmlsig.constants.DSigNs, 'X509SerialNumber')).text = str(
                 certificate.get_certificate().get_serial_number())
     signature_policy_identifier = etree.SubElement(
         signed_signature_properties,
         etree.QName(etsi, 'SignaturePolicyIdentifier'))
     signature_policy_id = etree.SubElement(
         signature_policy_identifier,
         etree.QName(etsi, 'SignaturePolicyId'))
     sig_policy_id = etree.SubElement(signature_policy_id,
                                      etree.QName(etsi, 'SigPolicyId'))
     etree.SubElement(sig_policy_id, etree.QName(
         etsi, 'Identifier')).text = sig_policy_identifier
     etree.SubElement(sig_policy_id, etree.QName(
         etsi, 'Description')).text = "Política de Firma FacturaE v3.1"
     sig_policy_hash = etree.SubElement(
         signature_policy_id, etree.QName(etsi, 'SigPolicyHash'))
     etree.SubElement(
         sig_policy_hash,
         etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
         attrib={'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'})
     try:
         remote = urllib.request.urlopen(sig_policy_identifier)
         hash_value = base64.b64encode(
             hashlib.sha1(remote.read()).digest())
     except urllib.request.HTTPError:
         hash_value = sig_policy_hash_value
     etree.SubElement(
         sig_policy_hash,
         etree.QName(xmlsig.constants.DSigNs,
                     'DigestValue')).text = hash_value
     signer_role = etree.SubElement(signed_signature_properties,
                                    etree.QName(etsi, 'SignerRole'))
     claimed_roles = etree.SubElement(signer_role,
                                      etree.QName(etsi, 'ClaimedRoles'))
     etree.SubElement(claimed_roles,
                      etree.QName(etsi,
                                  'ClaimedRole')).text = 'supplier'
     signed_data_object_properties = etree.SubElement(
         signed_properties,
         etree.QName(etsi, 'SignedDataObjectProperties'))
     data_object_format = etree.SubElement(
         signed_data_object_properties,
         etree.QName(etsi, 'DataObjectFormat'),
         attrib={'ObjectReference': '#' + reference_id})
     etree.SubElement(data_object_format,
                      etree.QName(etsi, 'Description')).text = 'Factura'
     etree.SubElement(data_object_format,
                      etree.QName(etsi, 'MimeType')).text = 'text/xml'
     ctx = xmlsig.SignatureContext()
     key = crypto.load_pkcs12(base64.b64decode(cert), password)
     ctx.x509 = key.get_certificate().to_cryptography()
     ctx.public_key = ctx.x509.public_key()
     ctx.private_key = key.get_privatekey().to_cryptography_key()
     root.append(sign)
     ctx.sign(sign)
     return etree.tostring(root, xml_declaration=True, encoding='UTF-8')
예제 #15
0
def sign_file(cert, password, xml_firma):
    random_val = random.randint(1, 99999)

    signature_id = 'Signature-%s' % random_val
    signed_properties_id = 'SignedProperties-%s' % signature_id
    signature_value = 'SignatureValue-%s' % random_val
    qualifying_properties = 'QualifyingProperties-%05d' % random_val
    key_info_id = 'KeyInfoId-%s' % signature_id
    reference_id = 'Reference-%05d' % random_val
    object_id = 'XadesObjectId-%05d' % random_val

    xades = 'http://uri.etsi.org/01903/v1.3.2#'
    ds = 'http://www.w3.org/2000/09/xmldsig#'
    xades141 = 'http://uri.etsi.org/01903/v1.4.1#'
    sig_policy_identifier = 'http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf'
    sig_policy_hash_value = 'Ohixl6upD6av8N7pEvDABhEL6hM='

    root = xml_firma
    certificate = crypto.load_pkcs12(cert, password)

    sign = etree.Element(etree.QName(ds, 'Signature'),
                         nsmap={
                             'ds': ds,
                             'xades': 'http://uri.etsi.org/01903/v1.3.2#'
                         },
                         attrib={
                             xmlsig.constants.ID_ATTR: signature_id,
                         })

    signed_info = etree.SubElement(sign, etree.QName(ds, 'SignedInfo'))

    etree.SubElement(signed_info,
                     etree.QName(ds, 'CanonicalizationMethod'),
                     attrib={'Algorithm': xmlsig.constants.TransformInclC14N})

    etree.SubElement(signed_info,
                     etree.QName(ds, 'SignatureMethod'),
                     attrib={'Algorithm': xmlsig.constants.TransformRsaSha256})

    reference = etree.SubElement(signed_info,
                                 etree.QName(ds, 'Reference'),
                                 attrib={
                                     xmlsig.constants.ID_ATTR: reference_id,
                                     'URI': ''
                                 })

    transforms = etree.SubElement(
        reference,
        etree.QName(ds, 'Transforms'),
    )

    etree.SubElement(
        transforms,
        etree.QName(ds, 'Transform'),
        attrib={
            'Algorithm':
            'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
        })

    etree.SubElement(
        reference,
        etree.QName(ds, 'DigestMethod'),
        attrib={'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'})

    etree.SubElement(reference, etree.QName(ds, 'DigestValue'))

    sec_reference = etree.SubElement(signed_info,
                                     etree.QName(ds, 'Reference'),
                                     attrib={
                                         xmlsig.constants.ID_ATTR:
                                         'ReferenceKeyInfo',
                                         'URI': '#' + key_info_id
                                     })

    etree.SubElement(
        sec_reference,
        etree.QName(ds, 'DigestMethod'),
        attrib={'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'})

    digest_value2 = hashlib.sha256(
        crypto.dump_certificate(crypto.FILETYPE_ASN1,
                                certificate.get_certificate()))

    etree.SubElement(sec_reference, etree.QName(
        ds, 'DigestValue')).text = base64.b64encode(digest_value2.digest())

    tr_reference = etree.SubElement(
        signed_info,
        etree.QName(ds, 'Reference'),
        attrib={
            'Type': 'http://uri.etsi.org/01903#SignedProperties',
            'URI': '#' + signed_properties_id,
        })

    etree.SubElement(
        tr_reference,
        etree.QName(ds, 'DigestMethod'),
        attrib={'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'})

    digest_value3 = hashlib.sha256(
        crypto.dump_certificate(crypto.FILETYPE_ASN1,
                                certificate.get_certificate()))

    etree.SubElement(tr_reference, etree.QName(
        ds, 'DigestValue')).text = base64.b64encode(digest_value3.digest())

    etree.SubElement(sign,
                     etree.QName(ds, 'SignatureValue'),
                     attrib={xmlsig.constants.ID_ATTR: signature_value})

    key_info = etree.SubElement(sign,
                                etree.QName(ds, 'KeyInfo'),
                                attrib={xmlsig.constants.ID_ATTR: key_info_id})

    x509 = etree.SubElement(
        key_info,
        etree.QName(ds, 'X509Data'),
    )

    etree.SubElement(
        x509,
        etree.QName(ds, 'X509Certificate'),
    )

    etree.SubElement(
        key_info,
        etree.QName(ds, 'KeyValue'),
    )

    object_node = etree.SubElement(
        sign,
        etree.QName(xmlsig.constants.DSigNs, 'Object'),
        attrib={xmlsig.constants.ID_ATTR: object_id})

    qualifying_properties = etree.SubElement(
        object_node,
        etree.QName(xades, 'QualifyingProperties'),
        nsmap={
            'xades': xades,
            'xades141': xades141
        },
        attrib={
            xmlsig.constants.ID_ATTR: qualifying_properties,
            'Target': '#' + signature_id
        })

    signed_properties = etree.SubElement(
        qualifying_properties,
        etree.QName(xades, 'SignedProperties'),
        attrib={xmlsig.constants.ID_ATTR: signed_properties_id})

    signed_signature_properties = etree.SubElement(
        signed_properties, etree.QName(xades, 'SignedSignatureProperties'))

    etree.SubElement(
        signed_signature_properties,
        etree.QName(xades,
                    'SigningTime')).text = datetime.datetime.now().strftime(
                        '%Y-%m-%dT%H:%M:%S%z')

    signing_certificate = etree.SubElement(
        signed_signature_properties, etree.QName(xades, 'SigningCertificate'))

    signing_certificate_cert = etree.SubElement(signing_certificate,
                                                etree.QName(xades, 'Cert'))

    cert_digest = etree.SubElement(signing_certificate_cert,
                                   etree.QName(xades, 'CertDigest'))

    etree.SubElement(
        cert_digest,
        etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
        attrib={'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'})

    hash_cert = hashlib.sha256(
        crypto.dump_certificate(crypto.FILETYPE_ASN1,
                                certificate.get_certificate()))

    etree.SubElement(cert_digest,
                     etree.QName(xmlsig.constants.DSigNs,
                                 'DigestValue')).text = base64.b64encode(
                                     hash_cert.digest())

    issuer_serial = etree.SubElement(signing_certificate_cert,
                                     etree.QName(xades, 'IssuerSerial'))

    etree.SubElement(
        issuer_serial, etree.QName(
            xmlsig.constants.DSigNs,
            'X509IssuerName')).text = xmlsig.utils.get_rdns_name(
                certificate.get_certificate().to_cryptography().issuer.rdns)

    etree.SubElement(issuer_serial,
                     etree.QName(
                         xmlsig.constants.DSigNs,
                         'X509SerialNumber')).text = str(
                             certificate.get_certificate().get_serial_number())

    signature_policy_identifier = etree.SubElement(
        signed_signature_properties,
        etree.QName(xades, 'SignaturePolicyIdentifier'))

    signature_policy_id = etree.SubElement(
        signature_policy_identifier, etree.QName(xades, 'SignaturePolicyId'))

    sig_policy_id = etree.SubElement(signature_policy_id,
                                     etree.QName(xades, 'SigPolicyId'))

    etree.SubElement(sig_policy_id,
                     etree.QName(xades,
                                 'Identifier')).text = sig_policy_identifier

    etree.SubElement(sig_policy_id,
                     etree.QName(xades, 'Description')).text = 'facturae31'

    sig_policy_hash = etree.SubElement(signature_policy_id,
                                       etree.QName(xades, 'SigPolicyHash'))

    etree.SubElement(
        sig_policy_hash,
        etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
        attrib={'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'})

    try:
        remote = urllib.urlopen(sig_policy_identifier)
        hash_value = base64.b64encode(hashlib.sha1(remote.read()).digest())
    except Exception as e:
        hash_value = sig_policy_hash_value

    etree.SubElement(sig_policy_hash,
                     etree.QName(xmlsig.constants.DSigNs,
                                 'DigestValue')).text = hash_value

    etsi = xades
    signer_role = etree.SubElement(signed_signature_properties,
                                   etree.QName(etsi, 'SignerRole'))
    claimed_roles = etree.SubElement(signer_role,
                                     etree.QName(etsi, 'ClaimedRoles'))

    etree.SubElement(claimed_roles, etree.QName(etsi,
                                                'ClaimedRole')).text = 'emisor'

    ctx = xmlsig.SignatureContext()
    key = crypto.load_pkcs12(cert, password)
    ctx.x509 = key.get_certificate().to_cryptography()
    ctx.public_key = ctx.x509.public_key()
    ctx.private_key = key.get_privatekey().to_cryptography_key()

    # print (etree.tostring(sign))
    root.append(sign)
    ctx.sign(sign)

    return etree.tostring(root,
                          encoding='UTF-8',
                          xml_declaration=True,
                          standalone=False)
예제 #16
0
def sign_envelope(envelope, public_cert, private_key, certfile, password):
    reference_id = "Reference"
    security = get_security_header(envelope)
    security.set(QName(ns.SOAP_ENV_11, "mustUnderstand"), "1")
    x509type = (
        "http://docs.oasis-open.org/wss/2004/01/"
        "oasis-200401-wss-x509-token-profile-1.0#X509v3"
    )
    encoding_type = (
        "http://docs.oasis-open.org/wss/2004/01/"
        "oasis-200401-wss-soap-message-security-1.0#Base64Binary"
    )
    binary_token = etree.SubElement(
        security,
        QName(ns.WSSE, "BinarySecurityToken"),
        attrib={
            QName(ns.WSU, "Id"): reference_id,
            "ValueType": x509type,
            "EncodingType": encoding_type,
        },
    )
    binary_token.text = base64.b64encode(
        public_cert.public_bytes(encoding=serialization.Encoding.DER)
    )
    signature = xmlsig.template.create(
        c14n_method=xmlsig.constants.TransformExclC14N,
        sign_method=xmlsig.constants.TransformRsaSha1,
        ns="ds",
    )
    envelope.append(signature)

    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
    # in this template with the actual certificate details when it signs.
    key_info = xmlsig.template.ensure_key_info(signature)
    sec_token_ref = etree.SubElement(key_info, QName(ns.WSSE, "SecurityTokenReference"))
    etree.SubElement(
        sec_token_ref,
        QName(ns.WSSE, "Reference"),
        attrib={"URI": "#" + reference_id, "ValueType": x509type},
    )
    # Insert the Signature node in the wsse:Security header.

    security.append(signature)

    # Perform the actual signing.
    ctx = xmlsig.SignatureContext()
    ctx.x509 = public_cert
    ctx.public_key = public_cert.public_key()
    ctx.private_key = private_key

    timestamp = etree.SubElement(security, QName(ns.WSU, "Timestamp"))
    now = datetime.now()
    etree.SubElement(timestamp, QName(ns.WSU, "Created")).text = now.strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )
    exp = now + timedelta(hours=1)
    etree.SubElement(timestamp, QName(ns.WSU, "Expires")).text = exp.strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )

    soap_env = detect_soap_env(envelope)
    _sign_node(ctx, signature, envelope.find(QName(soap_env, "Body")))
    _sign_node(ctx, signature, security.find(QName(ns.WSU, "Timestamp")))

    ctx.sign(signature)
    return etree.fromstring(etree.tostring(envelope, method="c14n"))
예제 #17
0
    def sign(root, certificate):
        """
        Sign XML with PKCS #12
        :author: Victor Laskurain <*****@*****.**>
        :return: SignatureValue
        """
        def create_node_tree(root_node, elem_list):
            """Convierte una lista en XML.

            Cada elemento de la lista se interpretará de la siguiente manera:

            Si es un string se añadirá al texto suelto del nodo root.
            Si es una tupla/lista t se interpretará como sigue:
                t[0]  es el nombre del elemento a crear. Puede tener prefijo de espacio
                de nombres.
                t[1]  es la lista de atributos donde las posiciones pares son claves y
                los impares valores.
                t[2:] son los subelementos que se interpretan recursivamente
            """
            for elem_def in elem_list:
                if isinstance(elem_def, str):
                    root_node.text = (root_node.text or '') + elem_def
                else:
                    ns = ''
                    elemname = elem_def[0]
                    attrs = elem_def[1]
                    children = elem_def[2:]
                    if ':' in elemname:
                        ns, elemname = elemname.split(':')
                        ns = root_node.nsmap[ns]
                    node = xmlsig.utils.create_node(elemname, root_node, ns)
                    for attr_name, attr_value in zip(attrs[::2], attrs[1::2]):
                        node.set(attr_name, attr_value)
                    create_node_tree(node, children)

        doc_id = 'id-' + str(uuid4())
        signature_id = 'sig-' + doc_id
        kinfo_id = 'ki-' + doc_id
        sp_id = 'sp-' + doc_id
        signature = xmlsig.template.create(
            xmlsig.constants.TransformInclC14N,
            xmlsig.constants.TransformRsaSha256,
            signature_id,
        )
        ref = xmlsig.template.add_reference(signature,
                                            xmlsig.constants.TransformSha256,
                                            uri='')
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
        xmlsig.template.add_reference(signature,
                                      xmlsig.constants.TransformSha256,
                                      uri='#' + kinfo_id)
        xmlsig.template.add_reference(signature,
                                      xmlsig.constants.TransformSha256,
                                      uri="#" + sp_id)
        ki = xmlsig.template.ensure_key_info(signature, name=kinfo_id)
        data = xmlsig.template.add_x509_data(ki)
        xmlsig.template.x509_data_add_certificate(data)
        xmlsig.template.add_key_value(ki)
        ctx = xmlsig.SignatureContext()
        ctx.load_pkcs12(certificate)
        ctx.x509 = certificate.get_certificate().to_cryptography()
        ctx.public_key = ctx.x509.public_key()
        ctx.private_key = certificate.get_privatekey().to_cryptography_key()
        dslist = ('ds:Object', (),
                  ('etsi:QualifyingProperties', ('Target', signature_id),
                   ('etsi:SignedProperties', ('Id', sp_id),
                    ('etsi:SignedSignatureProperties', (),
                     ('etsi:SigningTime', (), datetime.now().isoformat()),
                     ('etsi:SigningCertificateV2', (),
                      ('etsi:Cert', (),
                       ('etsi:CertDigest', (),
                        ('ds:DigestMethod',
                         ('Algorithm',
                          'http://www.w3.org/2000/09/xmldsig#sha256')),
                        ('ds:DigestValue', (),
                         b64encode(ctx.x509.fingerprint(
                             hashes.SHA256())).decode())))),
                     ('etsi:SignaturePolicyIdentifier', (),
                      ('etsi:SignaturePolicyId', (),
                       ('etsi:SigPolicyId', (),
                        ('etsi:Identifier', (),
                         'http://ticketbai.eus/politicafirma'),
                        ('etsi:Description', (),
                         'Política de Firma TicketBAI 1.0')),
                       ('etsi:SigPolicyHash', (),
                        ('ds:DigestMethod',
                         ('Algorithm',
                          'http://www.w3.org/2000/09/xmldsig#sha256')),
                        ('ds:DigestValue', (),
                         'lX1xDvBVAsPXkkJ7R07WCVbAm9e0H33I1sCpDtQNkbc='))))))))
        root.append(signature)
        create_node_tree(signature, [dslist])
        ctx.sign(signature)
        signature_value = signature.find(
            'ds:SignatureValue', namespaces=xmlsig.constants.NS_MAP).text
        # RFC2045 - Base64 Content-Transfer-Encoding (page 25)
        # Any characters outside of the base64 alphabet are to be ignored in
        # base64-encoded data.
        return signature_value.replace('\n', '')
예제 #18
0
    def _sign_file(self, move, request, public_cert, private_key):
        rand_min = 1
        rand_max = 99999
        signature_id = "Signature%05d" % random.randint(rand_min, rand_max)
        signed_properties_id = signature_id + "-SignedProperties%05d" % random.randint(
            rand_min, rand_max)
        key_info_id = "KeyInfo%05d" % random.randint(rand_min, rand_max)
        reference_id = "Reference%05d" % random.randint(rand_min, rand_max)
        object_id = "Object%05d" % random.randint(rand_min, rand_max)
        etsi = "http://uri.etsi.org/01903/v1.3.2#"
        sig_policy_identifier = ("http://www.facturae.es/"
                                 "politica_de_firma_formato_facturae/"
                                 "politica_de_firma_formato_facturae_v3_1"
                                 ".pdf")
        sig_policy_hash_value = "Ohixl6upD6av8N7pEvDABhEL6hM="
        root = etree.fromstring(request)
        sign = xmlsig.template.create(
            c14n_method=xmlsig.constants.TransformInclC14N,
            sign_method=xmlsig.constants.TransformRsaSha1,
            name=signature_id,
            ns="ds",
        )
        key_info = xmlsig.template.ensure_key_info(sign, name=key_info_id)
        x509_data = xmlsig.template.add_x509_data(key_info)
        xmlsig.template.x509_data_add_certificate(x509_data)
        xmlsig.template.add_key_value(key_info)
        with open(public_cert, "rb") as f:
            certificate = x509.load_pem_x509_certificate(f.read())
        xmlsig.template.add_reference(
            sign,
            xmlsig.constants.TransformSha1,
            uri="#" + signed_properties_id,
            uri_type="http://uri.etsi.org/01903#SignedProperties",
        )
        xmlsig.template.add_reference(sign,
                                      xmlsig.constants.TransformSha1,
                                      uri="#" + key_info_id)
        ref = xmlsig.template.add_reference(sign,
                                            xmlsig.constants.TransformSha1,
                                            name=reference_id,
                                            uri="")
        xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
        object_node = etree.SubElement(
            sign,
            etree.QName(xmlsig.constants.DSigNs, "Object"),
            nsmap={"etsi": etsi},
            attrib={xmlsig.constants.ID_ATTR: object_id},
        )
        qualifying_properties = etree.SubElement(
            object_node,
            etree.QName(etsi, "QualifyingProperties"),
            attrib={"Target": "#" + signature_id},
        )
        signed_properties = etree.SubElement(
            qualifying_properties,
            etree.QName(etsi, "SignedProperties"),
            attrib={xmlsig.constants.ID_ATTR: signed_properties_id},
        )
        signed_signature_properties = etree.SubElement(
            signed_properties, etree.QName(etsi, "SignedSignatureProperties"))
        now = datetime.now().replace(microsecond=0, tzinfo=pytz.utc)
        etree.SubElement(signed_signature_properties,
                         etree.QName(etsi,
                                     "SigningTime")).text = now.isoformat()
        signing_certificate = etree.SubElement(
            signed_signature_properties, etree.QName(etsi,
                                                     "SigningCertificate"))
        signing_certificate_cert = etree.SubElement(signing_certificate,
                                                    etree.QName(etsi, "Cert"))
        cert_digest = etree.SubElement(signing_certificate_cert,
                                       etree.QName(etsi, "CertDigest"))
        etree.SubElement(
            cert_digest,
            etree.QName(xmlsig.constants.DSigNs, "DigestMethod"),
            attrib={"Algorithm": "http://www.w3.org/2000/09/xmldsig#sha1"},
        )
        hash_cert = hashlib.sha1(certificate.public_bytes(Encoding.DER))
        etree.SubElement(cert_digest,
                         etree.QName(xmlsig.constants.DSigNs,
                                     "DigestValue")).text = base64.b64encode(
                                         hash_cert.digest())
        issuer_serial = etree.SubElement(signing_certificate_cert,
                                         etree.QName(etsi, "IssuerSerial"))
        etree.SubElement(
            issuer_serial,
            etree.QName(xmlsig.constants.DSigNs,
                        "X509IssuerName")).text = xmlsig.utils.get_rdns_name(
                            certificate.issuer.rdns)
        etree.SubElement(
            issuer_serial,
            etree.QName(xmlsig.constants.DSigNs,
                        "X509SerialNumber")).text = str(
                            certificate.serial_number)
        signature_policy_identifier = etree.SubElement(
            signed_signature_properties,
            etree.QName(etsi, "SignaturePolicyIdentifier"),
        )
        signature_policy_id = etree.SubElement(
            signature_policy_identifier, etree.QName(etsi,
                                                     "SignaturePolicyId"))
        sig_policy_id = etree.SubElement(signature_policy_id,
                                         etree.QName(etsi, "SigPolicyId"))
        etree.SubElement(sig_policy_id, etree.QName(
            etsi, "Identifier")).text = sig_policy_identifier
        etree.SubElement(sig_policy_id, etree.QName(
            etsi, "Description")).text = "Política de Firma FacturaE v3.1"
        sig_policy_hash = etree.SubElement(signature_policy_id,
                                           etree.QName(etsi, "SigPolicyHash"))
        etree.SubElement(
            sig_policy_hash,
            etree.QName(xmlsig.constants.DSigNs, "DigestMethod"),
            attrib={"Algorithm": "http://www.w3.org/2000/09/xmldsig#sha1"},
        )
        hash_value = sig_policy_hash_value
        etree.SubElement(sig_policy_hash,
                         etree.QName(xmlsig.constants.DSigNs,
                                     "DigestValue")).text = hash_value
        signer_role = etree.SubElement(signed_signature_properties,
                                       etree.QName(etsi, "SignerRole"))
        claimed_roles = etree.SubElement(signer_role,
                                         etree.QName(etsi, "ClaimedRoles"))
        etree.SubElement(claimed_roles,
                         etree.QName(etsi, "ClaimedRole")).text = "supplier"
        signed_data_object_properties = etree.SubElement(
            signed_properties, etree.QName(etsi, "SignedDataObjectProperties"))
        data_object_format = etree.SubElement(
            signed_data_object_properties,
            etree.QName(etsi, "DataObjectFormat"),
            attrib={"ObjectReference": "#" + reference_id},
        )
        etree.SubElement(data_object_format,
                         etree.QName(etsi, "Description")).text = "Factura"
        etree.SubElement(data_object_format,
                         etree.QName(etsi, "MimeType")).text = "text/xml"
        ctx = xmlsig.SignatureContext()

        ctx.x509 = certificate
        ctx.public_key = certificate.public_key()
        with open(private_key, "rb") as f:
            ctx.private_key = serialization.load_pem_private_key(f.read(),
                                                                 password=None)
        root.append(sign)
        ctx.sign(sign)
        return etree.tostring(root, xml_declaration=True, encoding="UTF-8")