示例#1
0
def _digest(data, hash_alg):
    """
    Calculate a hash digest of algorithm hash_alg and return the result base64 encoded.

    :param hash_alg: String with algorithm, such as 'sha1'
    :param data: The data to digest
    :returns: Base64 string
    """
    h = getattr(hashlib, hash_alg)()
    h.update(data)
    digest = b64e(h.digest())
    return digest
示例#2
0
def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path=".//{%s}Signature" % NS['ds']):
    """
    Sign an XML document. This means to 'complete' all Signature elements in the XML.

    :param t: XML as lxml.etree
    :param key_spec: private key reference, see xmlsec.crypto.from_keyspec() for syntax.
    :param cert_spec: None or public key reference (to add cert to document),
                      see xmlsec.crypto.from_keyspec() for syntax.
    :param sig_path: An xpath expression identifying the Signature template element
    :param reference_uri: Envelope signature reference URI
    :param insert_index: Insertion point for the Signature element,
                         Signature is inserted at beginning by default
    :returns: XML as lxml.etree (for convenience, 't' is modified in-place)
    """
    private = xmlsec.crypto.from_keyspec(key_spec, private=True)

    public = None
    if cert_spec is not None:
        public = xmlsec.crypto.from_keyspec(cert_spec)
        if public is None:
            raise XMLSigException("Unable to load public key from '%s'" % cert_spec)
        if public.keysize and private.keysize:  # XXX maybe one set and one not set should also raise exception?
            if public.keysize != private.keysize:
                raise XMLSigException("Public and private key sizes do not match ({!s}, {!s})".format(
                                      public.keysize, private.keysize))
            # This might be incorrect for PKCS#11 tokens if we have no public key
            log.debug("Using {!s} bit key".format(private.keysize))
    sig_paths = t.findall(sig_path)
    templates = list(filter(_is_template, sig_paths))
    if not templates:
        tmpl = add_enveloped_signature(t, reference_uri=reference_uri, pos=insert_index)
        templates = [tmpl]

    assert templates, XMLSigException("Failed to both find and add a signing template")

    if config.debug_write_to_files:
        with open("/tmp/sig-ref.xml", "w") as fd:
            fd.write(etree_to_string(root_elt(t)))

    for sig in templates:
        log.debug("processing sig template: %s" % etree.tostring(sig))
        si = sig.find(".//{%s}SignedInfo" % NS['ds'])
        assert si is not None
        cm_alg = _cm_alg(si)
        sig_alg = _sig_alg(si)

        _process_references(t, sig, verify_mode=False, sig_path=sig_path)
        # XXX create signature reference duplicates/overlaps process references unless a c14 is part of transforms
        log.debug("transform %s on %s" % (cm_alg, etree.tostring(si)))
        sic = _transform(cm_alg, si)
        log.debug("SignedInfo C14N: %s" % sic)

        # sign hash digest and insert it into the XML
        if private.do_digest:
            digest = xmlsec.crypto._digest(sic, sig_alg)
            log.debug("SignedInfo digest: %s" % digest)
            b_digest = b64d(digest)
            tbs = _signed_value(b_digest, private.keysize, private.do_padding, sig_alg)
        else:
            tbs = sic

        signed = private.sign(tbs, sig_alg)
        signature = b64e(signed)
        if isinstance(signature, six.binary_type):
            signature = six.text_type(signature, 'utf-8')
        log.debug("SignatureValue: %s" % signature)
        sv = sig.find(".//{%s}SignatureValue" % NS['ds'])
        if sv is None:
            si.addnext(DS.SignatureValue(signature))
        else:
            sv.text = signature

        for cert_src in (public, private):
            if cert_src is not None and cert_src.cert_pem:
                # Insert cert_data as b64-encoded X.509 certificate into XML document
                sv_elt = si.getnext()
                sv_elt.addnext(DS.KeyInfo(DS.X509Data(DS.X509Certificate(pem2b64(cert_src.cert_pem)))))
                break  # add the first we find, no more

    return t
示例#3
0
def sign(t,
         key_spec,
         cert_spec=None,
         reference_uri='',
         sig_path=".//{%s}Signature" % NS['ds']):
    """
    Sign an XML document. This means to 'complete' all Signature elements in the XML.

    :param t: XML as lxml.etree
    :param key_spec: private key reference, see _load_keyspec() for syntax.
    :param cert_spec: None or public key reference (to add cert to document), see _load_keyspec() for syntax.
    :param reference_uri: Envelope signature reference URI
    :returns: XML as lxml.etree (for convenience, 't' is modified in-place)
    """
    do_padding = False  # only in the case of our fallback keytype do we need to do pkcs1 padding here

    private = _load_keyspec(key_spec, private=True)
    if private is None:
        raise XMLSigException("Unable to load private key from '%s'" %
                              key_spec)

    if private['source'] == 'file':
        do_padding = True  # need to do p1 padding in this case

    public = None
    if cert_spec is not None:
        public = _load_keyspec(cert_spec)
        if public is None:
            raise XMLSigException("Unable to load public key from '%s'" %
                                  cert_spec)
        if public['keysize'] != private['keysize']:
            raise XMLSigException(
                "Public and private key sizes do not match (%s, %s)" %
                (public['keysize'], private['keysize']))
        # This might be incorrect for PKCS#11 tokens if we have no public key
        logging.debug("Using %s bit key" % (private['keysize']))

    if t.find(sig_path) is None:
        add_enveloped_signature(t, reference_uri=reference_uri)

    if config.debug_write_to_files:
        with open("/tmp/sig-ref.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    for sig in t.findall(sig_path):
        logging.debug("processing sig template: %s" % etree.tostring(sig))
        si = sig.find(".//{%s}SignedInfo" % NS['ds'])
        cm_alg = _cm_alg(si)
        digest_alg = _sig_digest(si)

        _process_references(t, sig, return_verified=False, sig_path=sig_path)
        # XXX create signature reference duplicates/overlaps process references unless a c14 is part of transforms
        b_digest = _create_signature_digest(si, cm_alg, digest_alg)

        # sign hash digest and insert it into the XML
        tbs = _signed_value(b_digest, private.get('keysize'), do_padding,
                            digest_alg)
        signed = private['f_private'](tbs)
        signature = b64e(signed)
        logging.debug("SignatureValue: %s" % signature)
        sv = sig.find(".//{%s}SignatureValue" % NS['ds'])
        if sv is None:
            si.addnext(DS.SignatureValue(signature))
        else:
            sv.text = signature

        if public is not None:
            # Insert cert_data as b64-encoded X.509 certificate into XML document
            sv_elt = si.getnext()
            sv_elt.addnext(
                DS.KeyInfo(
                    DS.X509Data(DS.X509Certificate(pem2b64(public['data'])))))

    return t
示例#4
0
def sign(t,
         key_spec,
         cert_spec=None,
         reference_uri='',
         insert_index=0,
         sig_path=".//{%s}Signature" % NS['ds']):
    """
    Sign an XML document. This means to 'complete' all Signature elements in the XML.

    :param t: XML as lxml.etree
    :param key_spec: private key reference, see xmlsec.crypto.from_keyspec() for syntax.
    :param cert_spec: None or public key reference (to add cert to document),
                      see xmlsec.crypto.from_keyspec() for syntax.
    :param sig_path: An xpath expression identifying the Signature template element
    :param reference_uri: Envelope signature reference URI
    :param insert_index: Insertion point for the Signature element,
                         Signature is inserted at beginning by default
    :returns: XML as lxml.etree (for convenience, 't' is modified in-place)
    """
    private = xmlsec.crypto.from_keyspec(key_spec, private=True)

    public = None
    if cert_spec is not None:
        public = xmlsec.crypto.from_keyspec(cert_spec)
        if public is None:
            raise XMLSigException("Unable to load public key from '%s'" %
                                  cert_spec)
        if public.keysize and private.keysize:  # XXX maybe one set and one not set should also raise exception?
            if public.keysize != private.keysize:
                raise XMLSigException(
                    "Public and private key sizes do not match ({!s}, {!s})".
                    format(public.keysize, private.keysize))
            # This might be incorrect for PKCS#11 tokens if we have no public key
            logging.debug("Using {!s} bit key".format(private.keysize))

    templates = filter(_is_template, t.findall(sig_path))
    if not templates:
        tmpl = add_enveloped_signature(t,
                                       reference_uri=reference_uri,
                                       pos=insert_index)
        templates = [tmpl]

    assert templates, XMLSigException(
        "Failed to both find and add a signing template")

    if config.debug_write_to_files:
        with open("/tmp/sig-ref.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    for sig in templates:
        logging.debug("processing sig template: %s" % etree.tostring(sig))
        si = sig.find(".//{%s}SignedInfo" % NS['ds'])
        assert si is not None
        cm_alg = _cm_alg(si)
        digest_alg = _sig_digest(si)

        _process_references(t, sig, return_verified=False, sig_path=sig_path)
        # XXX create signature reference duplicates/overlaps process references unless a c14 is part of transforms
        b_digest = _create_signature_digest(si, cm_alg, digest_alg)

        # sign hash digest and insert it into the XML
        tbs = _signed_value(b_digest, private.keysize, private.do_padding,
                            digest_alg)
        signed = private.sign(tbs)
        signature = b64e(signed)
        logging.debug("SignatureValue: %s" % signature)
        sv = sig.find(".//{%s}SignatureValue" % NS['ds'])
        if sv is None:
            si.addnext(DS.SignatureValue(signature))
        else:
            sv.text = signature

        for cert_src in (public, private):
            if cert_src is not None and cert_src.cert_pem:
                # Insert cert_data as b64-encoded X.509 certificate into XML document
                sv_elt = si.getnext()
                sv_elt.addnext(
                    DS.KeyInfo(
                        DS.X509Data(
                            DS.X509Certificate(pem2b64(cert_src.cert_pem)))))
                break  # add the first we find, no more

    return t
示例#5
0
def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path=".//{%s}Signature" % NS['ds']):
    """
    Sign an XML document. This means to 'complete' all Signature elements in the XML.

    :param t: XML as lxml.etree
    :param key_spec: private key reference, see _load_keyspec() for syntax.
    :param cert_spec: None or public key reference (to add cert to document), see _load_keyspec() for syntax.
    :param reference_uri: Envelope signature reference URI
    :param insert_index: Insertion point for the Signature element,
                         Signature is inserted at beginning by default
    :returns: XML as lxml.etree (for convenience, 't' is modified in-place)
    """
    do_padding = False  # only in the case of our fallback keytype do we need to do pkcs1 padding here

    private = _load_keyspec(key_spec, private=True)
    if private is None:
        raise XMLSigException("Unable to load private key from '%s'" % key_spec)

    if private['source'] == 'file':
        do_padding = True  # need to do p1 padding in this case

    public = None
    if cert_spec is not None:
        public = _load_keyspec(cert_spec)
        if public is None:
            raise XMLSigException("Unable to load public key from '%s'" % cert_spec)
        if public['keysize'] != private['keysize']:
            raise XMLSigException("Public and private key sizes do not match (%s, %s)"
                                  % (public['keysize'], private['keysize']))
        # This might be incorrect for PKCS#11 tokens if we have no public key
        logging.debug("Using %s bit key" % (private['keysize']))

    if t.find(sig_path) is None:
        add_enveloped_signature(t, reference_uri=reference_uri, pos=insert_index)

    if config.debug_write_to_files:
        with open("/tmp/sig-ref.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    for sig in t.findall(sig_path):
        logging.debug("processing sig template: %s" % etree.tostring(sig))
        si = sig.find(".//{%s}SignedInfo" % NS['ds'])
        cm_alg = _cm_alg(si)
        digest_alg = _sig_digest(si)

        _process_references(t, sig, return_verified=False, sig_path=sig_path)
        # XXX create signature reference duplicates/overlaps process references unless a c14 is part of transforms
        b_digest = _create_signature_digest(si, cm_alg, digest_alg)

        # sign hash digest and insert it into the XML
        tbs = _signed_value(b_digest, private.get('keysize'), do_padding, digest_alg)
        signed = private['f_private'](tbs)
        signature = b64e(signed)
        logging.debug("SignatureValue: %s" % signature)
        sv = sig.find(".//{%s}SignatureValue" % NS['ds'])
        if sv is None:
            si.addnext(DS.SignatureValue(signature.decode('ascii')))
        else:
            sv.text = signature

        if public is not None:
            # Insert cert_data as b64-encoded X.509 certificate into XML document
            sv_elt = si.getnext()
            sv_elt.addnext(
                DS.KeyInfo(
                    DS.X509Data(
                        DS.X509Certificate(
                            pem2b64(
                                public['data']
                            ).decode('ascii')
                        )
                    )
                )
            )

    return t