Exemplo n.º 1
0
def add_enveloped_signature(t,
                            c14n_method=config.default_c14n_alg,
                            digest_alg=config.default_digest_alg,
                            signature_alg=config.default_signature_alg,
                            transforms=None,
                            reference_uri='',
                            pos=0):
    if transforms is None:
        transforms = (constants.TRANSFORM_ENVELOPED_SIGNATURE,
                      constants.TRANSFORM_C14N_EXCLUSIVE_WITH_COMMENTS)

    tmpl = _enveloped_signature_template(c14n_method, digest_alg, transforms, reference_uri, signature_alg)
    if pos == -1:
        root_elt(t).append(tmpl)
    else:
        root_elt(t).insert(pos, tmpl)
Exemplo n.º 2
0
def _c14n(t, exclusive, with_comments, inclusive_prefix_list=None, schema=None):
    """
    Perform XML canonicalization (c14n) on an lxml.etree.

    :param t: XML as lxml.etree
    :param exclusive: boolean
    :param with_comments: boolean, keep comments or not
    :param inclusive_prefix_list: List of namespaces to include (?)
    :returns: XML as string (utf8)
    """
    doc = t
    if root_elt(doc).getparent() is not None:
        xml_str = etree_to_string(doc)
        doc = parse_xml(xml_str, remove_whitespace=config.c14n_strip_ws, remove_comments=not with_comments, schema=schema)
        del xml_str

    buf = six.text_type(
        etree.tostring(doc,
                       method='c14n',
                       exclusive=exclusive,
                       with_comments=with_comments,
                       inclusive_ns_prefixes=inclusive_prefix_list),
        'utf-8')
    #u = unescape_xml_entities(buf.decode("utf8", 'strict')).encode("utf8").strip()
    assert buf[0] == '<'
    assert buf[-1] == '>'
    #if u[0] != '<':
    #    raise XMLSigException("C14N buffer doesn't start with '<'")
    #if u[-1] != '>':
    #    raise XMLSigException("C14N buffer doesn't end with '>'")
    #return u
    return buf
Exemplo n.º 3
0
def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds']):
    """
    Verify the signature(s) in an XML document.

    Throws an XMLSigException on any non-matching signatures.

    :param t: XML as lxml.etree
    :param keyspec: X.509 cert filename, string with fingerprint or X.509 cert as string
    :returns: True if signature(s) validated, False if there were no signatures
    """
    if config.debug_write_to_files:
        with open("/tmp/foo-sig.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    # Load and parse certificate, unless keyspec is a fingerprint.
    cert = _load_keyspec(keyspec)

    validated = []
    for sig in t.findall(sig_path):
        sv = sig.findtext(".//{%s}SignatureValue" % NS['ds'])
        if sv is None:
            raise XMLSigException("No SignatureValue")

        this_f_public = None
        this_keysize = None
        if cert is None:
            # keyspec is fingerprint - look for matching certificate in XML
            this_cert = _load_keyspec(keyspec, signature_element=sig)
            if this_cert is None:
                raise XMLSigException(
                    "Could not find certificate to validate signature")
            this_f_public = this_cert['f_public']
            this_keysize = this_cert['keysize']
        else:
            # Non-fingerprint keyspec, use pre-parsed values
            this_cert = cert
            this_f_public = cert['f_public']
            this_keysize = cert['keysize']

        logging.debug("key size: %d bits" % this_cert['keysize'])

        if this_cert is None:
            raise XMLSigException(
                "Could not find certificate to validate signature")

        si = sig.find(".//{%s}SignedInfo" % NS['ds'])
        cm_alg = _cm_alg(si)
        digest_alg = _sig_digest(si)

        validated_objects = _process_references(t, sig, sig_path)
        b_digest = _create_signature_digest(si, cm_alg, digest_alg)
        actual = _signed_value(b_digest, this_keysize, True, digest_alg)
        expected = this_f_public(b64d(sv))

        if expected != actual:
            raise XMLSigException("Signature validation failed")
        validated.extend(validated_objects)

    return validated
Exemplo n.º 4
0
def add_enveloped_signature(t,
                            c14n_method=config.default_c14n_alg,
                            digest_alg=config.default_digest_alg,
                            signature_alg=config.default_signature_alg,
                            transforms=None,
                            reference_uri='',
                            pos=0):
    if transforms is None:
        transforms = (constants.TRANSFORM_ENVELOPED_SIGNATURE,
                      constants.TRANSFORM_C14N_EXCLUSIVE_WITH_COMMENTS)

    tmpl = _enveloped_signature_template(c14n_method, digest_alg, transforms,
                                         reference_uri, signature_alg)
    if pos == -1:
        root_elt(t).append(tmpl)
    else:
        root_elt(t).insert(pos, tmpl)
Exemplo n.º 5
0
def _process_references(t, sig, return_verified=True, sig_path=".//{%s}Signature" % NS['ds']):
    """
    :returns: hash algorithm as string
    """

    verified_objects = []
    for ref in sig.findall(".//{%s}Reference" % NS['ds']):
        obj = None
        hash_alg = None
        uri = ref.get('URI', None)
        if uri is None or uri == '#' or uri == '':
            ct = _remove_child_comments(_implicit_same_document(t, sig))
            obj = root_elt(ct)
        elif uri.startswith('#'):
            ct = copy.deepcopy(t)
            obj = _remove_child_comments(_get_by_id(ct, uri[1:]))
        else:
            raise XMLSigException("Unknown reference %s" % uri)

        if obj is None:
            raise XMLSigException("Unable to dereference Reference URI='%s'" % uri)

        if return_verified:
            verified_objects.append(copy.deepcopy(obj))

        if config.debug_write_to_files:
            with open("/tmp/foo-pre-transform.xml", "w") as fd:
                fd.write(etree.tostring(obj))

        for tr in ref.findall(".//{%s}Transform" % NS['ds']):
            logging.debug("transform: %s" % _alg(tr))
            obj = _transform(_alg(tr), obj, tr=tr, sig_path=sig_path)

        if not isinstance(obj, bytes):
            if config.debug_write_to_files:
                with open("/tmp/foo-pre-serialize.xml", "w") as fd:
                    fd.write(etree.tostring(obj))
            obj = _transform(constants.TRANSFORM_C14N_INCLUSIVE, obj)

        if config.debug_write_to_files:
            with open("/tmp/foo-obj.xml", "w") as fd:
                fd.write(obj)

        dm = ref.find(".//{%s}DigestMethod" % NS['ds'])
        if dm is None:
            raise XMLSigException("Unable to find DigestMethod")
        hash_alg = (_alg(dm).split("#"))[1]
        logging.debug("using hash algorithm %s" % hash_alg)
        digest = _digest(obj, hash_alg)
        logging.debug("using digest %s (%s) for ref %s" % (digest, hash_alg, uri))
        dv = ref.find(".//{%s}DigestValue" % NS['ds'])
        logging.debug(etree.tostring(dv))
        dv.text = digest

    if return_verified:
        return verified_objects
    else:
        return None
Exemplo n.º 6
0
def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds']):
    """
    Verify the signature(s) in an XML document.

    Throws an XMLSigException on any non-matching signatures.

    :param t: XML as lxml.etree
    :param keyspec: X.509 cert filename, string with fingerprint or X.509 cert as string
    :returns: True if signature(s) validated, False if there were no signatures
    """
    if config.debug_write_to_files:
        with open("/tmp/foo-sig.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    # Load and parse certificate, unless keyspec is a fingerprint.
    cert = _load_keyspec(keyspec)

    validated = []
    for sig in t.findall(sig_path):
        sv = sig.findtext(".//{%s}SignatureValue" % NS['ds'])
        if sv is None:
            raise XMLSigException("No SignatureValue")

        this_f_public = None
        this_keysize = None
        if cert is None:
            # keyspec is fingerprint - look for matching certificate in XML
            this_cert = _load_keyspec(keyspec, signature_element=sig)
            if this_cert is None:
                raise XMLSigException("Could not find certificate to validate signature")
            this_f_public = this_cert['f_public']
            this_keysize = this_cert['keysize']
        else:
            # Non-fingerprint keyspec, use pre-parsed values
            this_cert = cert
            this_f_public = cert['f_public']
            this_keysize = cert['keysize']

        logging.debug("key size: %d bits" % this_cert['keysize'])

        if this_cert is None:
            raise XMLSigException("Could not find certificate to validate signature")

        si = sig.find(".//{%s}SignedInfo" % NS['ds'])
        cm_alg = _cm_alg(si)
        digest_alg = _sig_digest(si)

        validated_objects = _process_references(t, sig, sig_path)
        b_digest = _create_signature_digest(si, cm_alg, digest_alg)
        actual = _signed_value(b_digest, this_keysize, True, digest_alg)
        expected = this_f_public(b64d(sv))

        if not compare_digest(expected, actual):
            raise XMLSigException("Signature validation failed")
        validated.extend(validated_objects)

    return validated
Exemplo n.º 7
0
def _verify(t,
            keyspec,
            sig_path=".//{%s}Signature" % NS['ds'],
            drop_signature=False):
    """
    Verify the signature(s) in an XML document.

    Throws an XMLSigException on any non-matching signatures.

    :param t: XML as lxml.etree
    :param keyspec: X.509 cert filename, string with fingerprint or X.509 cert as string
    :returns: True if signature(s) validated, False if there were no signatures
    """
    if config.debug_write_to_files:
        with open("/tmp/foo-sig.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    validated = []
    for sig in t.findall(sig_path):
        try:
            sv = sig.findtext(".//{%s}SignatureValue" % NS['ds'])
            if not sv:
                raise XMLSigException("No SignatureValue")

            logging.debug("SignatureValue: {!s}".format(sv))
            this_cert = xmlsec.crypto.from_keyspec(keyspec,
                                                   signature_element=sig)
            logging.debug("key size: {!s} bits".format(this_cert.keysize))

            si = sig.find(".//{%s}SignedInfo" % NS['ds'])
            logging.debug("Found signedinfo {!s}".format(etree.tostring(si)))
            cm_alg = _cm_alg(si)
            sig_digest_alg = _sig_digest(si)

            refmap = _process_references(t,
                                         sig,
                                         sig_path=sig_path,
                                         drop_signature=drop_signature)
            for ref, obj in refmap.items():
                b_digest = _create_signature_digest(si, cm_alg, sig_digest_alg)
                actual = _signed_value(b_digest, this_cert.keysize, True,
                                       sig_digest_alg)
                if not this_cert.verify(b64d(sv), actual):
                    raise XMLSigException(
                        "Failed to validate {!s} using ref digest {!s} and cm {!s}"
                        .format(etree.tostring(sig), ref_digest_alg, cm_alg))
                validated.append(obj)
        except XMLSigException, ex:
            logging.error(ex)
Exemplo n.º 8
0
def _c14n(t,
          exclusive,
          with_comments,
          inclusive_prefix_list=None,
          schema=None):
    """
    Perform XML canonicalization (c14n) on an lxml.etree.

    :param t: XML as lxml.etree
    :param exclusive: boolean
    :param with_comments: boolean, keep comments or not
    :param inclusive_prefix_list: List of namespaces to include (?)
    :returns: XML as string (utf8)
    """
    doc = t
    if root_elt(doc).getparent() is not None:
        xml_str = etree_to_string(doc)
        doc = parse_xml(xml_str,
                        remove_whitespace=config.c14n_strip_ws,
                        remove_comments=not with_comments,
                        schema=schema)
        del xml_str

    buf = six.text_type(
        etree.tostring(doc,
                       method='c14n',
                       exclusive=exclusive,
                       with_comments=with_comments,
                       inclusive_ns_prefixes=inclusive_prefix_list), 'utf-8')
    #u = unescape_xml_entities(buf.decode("utf8", 'strict')).encode("utf8").strip()
    assert buf[0] == '<'
    assert buf[-1] == '>'
    #if u[0] != '<':
    #    raise XMLSigException("C14N buffer doesn't start with '<'")
    #if u[-1] != '>':
    #    raise XMLSigException("C14N buffer doesn't end with '>'")
    #return u
    return buf
Exemplo n.º 9
0
def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=False):
    """
    Verify the signature(s) in an XML document.

    Throws an XMLSigException on any non-matching signatures.

    :param t: XML as lxml.etree
    :param keyspec: X.509 cert filename, string with fingerprint or X.509 cert as string
    :returns: True if signature(s) validated, False if there were no signatures
    """
    if config.debug_write_to_files:
        with open("/tmp/foo-sig.xml", "w") as fd:
            fd.write(etree.tostring(root_elt(t)))

    validated = []
    for sig in t.findall(sig_path):
        try:
            sv = sig.findtext(".//{%s}SignatureValue" % NS['ds'])
            if sv is None:
                raise XMLSigException("No SignatureValue")

            this_cert = xmlsec.crypto.from_keyspec(keyspec, signature_element=sig)
            logging.debug("key size: {!s} bits".format(this_cert.keysize))

            si = sig.find(".//{%s}SignedInfo" % NS['ds'])
            cm_alg = _cm_alg(si)
            digest_alg = _sig_digest(si)

            validated_objects = _process_references(t, sig, sig_path=sig_path, drop_signature=drop_signature)
            b_digest = _create_signature_digest(si, cm_alg, digest_alg)
            actual = _signed_value(b_digest, this_cert.keysize, True, digest_alg)
            if not this_cert.verify(b64d(sv), actual):
                raise XMLSigException("Failed to validate {!s}".format(etree.tostring(sig)))
            validated.extend(validated_objects)
        except XMLSigException, ex:
            logging.error(ex)
Exemplo n.º 10
0
def _process_references(t, sig, verify_mode=True, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=False):
    """
    :returns: hash algorithm as string
    """

    verified_objects = {}
    for ref in sig.findall(".//{%s}Reference" % NS['ds']):
        obj = None
        hash_alg = None
        uri = ref.get('URI', None)
        if uri is None or uri == '#' or uri == '':
            ref_obj = _implicit_same_document(t, sig)
            if ref_obj is None:
                raise XMLSigException("Unable to find reference while processing implicit same document reference")
            ct = _remove_child_comments(ref_obj)
            obj = root_elt(ct)
        elif uri.startswith('#'):
            ct = copy.deepcopy(t)
            ref_obj = _get_by_id(ct, uri[1:])
            if ref_obj is None:
                raise XMLSigException("Unable to find reference while processing '%s'" % uri)
            obj = _remove_child_comments(ref_obj)
        else:
            raise XMLSigException("Unknown reference %s" % uri)

        if obj is None:
            raise XMLSigException("Unable to dereference Reference URI='%s'" % uri)

        obj_copy = obj
        if verify_mode:
            obj_copy = copy.deepcopy(obj)
            if drop_signature:
                for sig in obj_copy.findall(sig_path):
                    sig.getparent().remove(sig)

        if config.debug_write_to_files:
            with open("/tmp/foo-pre-transform.xml", "w") as fd:
                fd.write(etree_to_string(obj))

        for tr in ref.findall(".//{%s}Transform" % NS['ds']):
            obj = _transform(_alg(tr), obj, tr=tr, sig_path=sig_path)
            nslist = _find_nslist(tr)
            if nslist is not None:
                r = root_elt(t)
                for nsprefix in nslist:
                    if nsprefix in r.nsmap:
                        obj_copy.nsmap[nsprefix] = r.nsmap[nsprefix]

        if not isinstance(obj, six.string_types):
            if config.debug_write_to_files:
                with open("/tmp/foo-pre-serialize.xml", "w") as fd:
                    fd.write(etree_to_string(obj))
            obj = _transform(constants.TRANSFORM_C14N_INCLUSIVE, obj)

        if config.debug_write_to_files:
            with open("/tmp/foo-obj.xml", "w") as fd:
                if six.PY2:
                    obj = obj.encode('utf-8')
                fd.write(obj)

        hash_alg = _ref_digest(ref)
        log.debug("using hash algorithm %s" % hash_alg)
        digest = xmlsec.crypto._digest(obj, hash_alg)
        log.debug("computed %s digest %s for ref %s" % (hash_alg, digest, uri))
        dv = ref.find(".//{%s}DigestValue" % NS['ds'])

        if verify_mode:
            log.debug("found %s digest %s for ref %s" % (hash_alg, dv.text, uri))
            computed_digest_binary = b64d(digest)
            digest_binary = b64d(dv.text)
            if digest_binary == computed_digest_binary: # no point in verifying signature if the digest doesn't match
                verified_objects[ref] = obj_copy
            else:
                log.error("not returning ref %s - digest mismatch" % uri)
        else: # signing - lets store the digest
            log.debug("replacing digest in %s" % etree.tostring(dv))
            dv.text = digest


    if verify_mode:
        return verified_objects
    else:
        return None
Exemplo n.º 11
0
def _implicit_same_document(t, sig):
    if config.same_document_is_root:
        return root_elt(copy.deepcopy(t))
    else:
        return copy.deepcopy(sig.getparent())
Exemplo n.º 12
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
Exemplo n.º 13
0
def _process_references(t,
                        sig,
                        return_verified=True,
                        sig_path=".//{%s}Signature" % NS['ds'],
                        drop_signature=False):
    """
    :returns: hash algorithm as string
    """

    verified_objects = {}
    for ref in sig.findall(".//{%s}Reference" % NS['ds']):
        obj = None
        hash_alg = None
        uri = ref.get('URI', None)
        if uri is None or uri == '#' or uri == '':
            ref_obj = _implicit_same_document(t, sig)
            if ref_obj is None:
                raise XMLSigException(
                    "Unable to find reference while processing implicit same document reference"
                )
            ct = _remove_child_comments(ref_obj)
            obj = root_elt(ct)
        elif uri.startswith('#'):
            ct = copy.deepcopy(t)
            ref_obj = _get_by_id(ct, uri[1:])
            if ref_obj is None:
                raise XMLSigException(
                    "Unable to find reference while processing '%s'" % uri)
            obj = _remove_child_comments(ref_obj)
        else:
            raise XMLSigException("Unknown reference %s" % uri)

        if obj is None:
            raise XMLSigException("Unable to dereference Reference URI='%s'" %
                                  uri)

        if return_verified:
            obj_copy = copy.deepcopy(obj)
            if drop_signature:
                for sig in obj_copy.findall(sig_path):
                    sig.getparent().remove(sig)
            verified_objects[ref] = obj_copy

        if config.debug_write_to_files:
            with open("/tmp/foo-pre-transform.xml", "w") as fd:
                fd.write(etree.tostring(obj))

        for tr in ref.findall(".//{%s}Transform" % NS['ds']):
            logging.debug("transform: %s" % _alg(tr))
            obj = _transform(_alg(tr), obj, tr=tr, sig_path=sig_path)
            nslist = _find_nslist(tr)
            if nslist is not None:
                r = root_elt(t)
                for nsprefix in nslist:
                    if nsprefix in r.nsmap:
                        obj_copy.nsmap[nsprefix] = r.nsmap[nsprefix]

        if not isinstance(obj, basestring):
            if config.debug_write_to_files:
                with open("/tmp/foo-pre-serialize.xml", "w") as fd:
                    fd.write(etree.tostring(obj))
            obj = _transform(constants.TRANSFORM_C14N_INCLUSIVE, obj)

        if config.debug_write_to_files:
            with open("/tmp/foo-obj.xml", "w") as fd:
                fd.write(obj)

        hash_alg = _ref_digest(ref)
        logging.debug("using hash algorithm %s" % hash_alg)
        digest = _digest(obj, hash_alg)
        logging.debug("using digest %s (%s) for ref %s" %
                      (digest, hash_alg, uri))
        dv = ref.find(".//{%s}DigestValue" % NS['ds'])
        #logging.debug(etree.tostring(dv))
        dv.text = digest

    if return_verified:
        return verified_objects
    else:
        return None
Exemplo n.º 14
0
def _process_references(t,
                        sig,
                        verify_mode=True,
                        sig_path=".//{%s}Signature" % NS['ds'],
                        drop_signature=False):
    """
    :returns: hash algorithm as string
    """

    verified_objects = {}
    for ref in sig.findall(".//{%s}Reference" % NS['ds']):
        obj = None
        hash_alg = None
        uri = ref.get('URI', None)
        if uri is None or uri == '#' or uri == '':
            ref_obj = _implicit_same_document(t, sig)
            if ref_obj is None:
                raise XMLSigException(
                    "Unable to find reference while processing implicit same document reference"
                )
            ct = _remove_child_comments(ref_obj)
            obj = root_elt(ct)
        elif uri.startswith('#'):
            ct = copy.deepcopy(t)
            ref_obj = _get_by_id(ct, uri[1:])
            if ref_obj is None:
                raise XMLSigException(
                    "Unable to find reference while processing '%s'" % uri)
            obj = _remove_child_comments(ref_obj)
        else:
            raise XMLSigException("Unknown reference %s" % uri)

        if obj is None:
            raise XMLSigException("Unable to dereference Reference URI='%s'" %
                                  uri)

        obj_copy = obj
        if verify_mode:
            obj_copy = copy.deepcopy(obj)
            if drop_signature:
                for sig in obj_copy.findall(sig_path):
                    sig.getparent().remove(sig)

        if config.debug_write_to_files:
            with open("/tmp/foo-pre-transform.xml", "w") as fd:
                fd.write(etree.tostring(obj))

        for tr in ref.findall(".//{%s}Transform" % NS['ds']):
            obj = _transform(_alg(tr), obj, tr=tr, sig_path=sig_path)
            nslist = _find_nslist(tr)
            if nslist is not None:
                r = root_elt(t)
                for nsprefix in nslist:
                    if nsprefix in r.nsmap:
                        obj_copy.nsmap[nsprefix] = r.nsmap[nsprefix]

        if not isinstance(obj, basestring):
            if config.debug_write_to_files:
                with open("/tmp/foo-pre-serialize.xml", "w") as fd:
                    fd.write(etree.tostring(obj))
            obj = _transform(constants.TRANSFORM_C14N_INCLUSIVE, obj)

        if config.debug_write_to_files:
            with open("/tmp/foo-obj.xml", "w") as fd:
                fd.write(obj)

        hash_alg = _ref_digest(ref)
        logging.debug("using hash algorithm %s" % hash_alg)
        digest = xmlsec.crypto._digest(obj, hash_alg)
        logging.debug("computed %s digest %s for ref %s" %
                      (hash_alg, digest, uri))
        dv = ref.find(".//{%s}DigestValue" % NS['ds'])

        if verify_mode:
            logging.debug("found %s digest %s for ref %s" %
                          (hash_alg, dv.text, uri))
            computed_digest_binary = b64d(digest)
            digest_binary = b64d(dv.text)
            if digest_binary == computed_digest_binary:  # no point in verifying signature if the digest doesn't match
                verified_objects[ref] = obj_copy
            else:
                logging.error("not returning ref %s - digest mismatch" % uri)
        else:  # signing - lets store the digest
            logging.debug("replacing digest in %s" % etree.tostring(dv))
            dv.text = digest

    if verify_mode:
        return verified_objects
    else:
        return None
Exemplo n.º 15
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
Exemplo n.º 16
0
def _process_references(t,
                        sig,
                        return_verified=True,
                        sig_path=".//{%s}Signature" % NS['ds']):
    """
    :returns: hash algorithm as string
    """

    verified_objects = []
    for ref in sig.findall(".//{%s}Reference" % NS['ds']):
        obj = None
        hash_alg = None
        uri = ref.get('URI', None)
        if uri is None or uri == '#' or uri == '':
            ct = _remove_child_comments(_implicit_same_document(t, sig))
            obj = root_elt(ct)
        elif uri.startswith('#'):
            ct = copy.deepcopy(t)
            obj = _remove_child_comments(_get_by_id(ct, uri[1:]))
        else:
            raise XMLSigException("Unknown reference %s" % uri)

        if obj is None:
            raise XMLSigException("Unable to dereference Reference URI='%s'" %
                                  uri)

        if return_verified:
            verified_objects.append(copy.deepcopy(obj))

        if config.debug_write_to_files:
            with open("/tmp/foo-pre-transform.xml", "w") as fd:
                fd.write(etree.tostring(obj))

        for tr in ref.findall(".//{%s}Transform" % NS['ds']):
            logging.debug("transform: %s" % _alg(tr))
            obj = _transform(_alg(tr), obj, tr=tr, sig_path=sig_path)

        if not isinstance(obj, basestring):
            if config.debug_write_to_files:
                with open("/tmp/foo-pre-serialize.xml", "w") as fd:
                    fd.write(etree.tostring(obj))
            obj = _transform(constants.TRANSFORM_C14N_INCLUSIVE, obj)

        if config.debug_write_to_files:
            with open("/tmp/foo-obj.xml", "w") as fd:
                fd.write(obj)

        dm = ref.find(".//{%s}DigestMethod" % NS['ds'])
        if dm is None:
            raise XMLSigException("Unable to find DigestMethod")
        hash_alg = (_alg(dm).split("#"))[1]
        logging.debug("using hash algorithm %s" % hash_alg)
        digest = _digest(obj, hash_alg)
        logging.debug("using digest %s (%s) for ref %s" %
                      (digest, hash_alg, uri))
        dv = ref.find(".//{%s}DigestValue" % NS['ds'])
        logging.debug(etree.tostring(dv))
        dv.text = digest

    if return_verified:
        return verified_objects
    else:
        return None
Exemplo n.º 17
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
Exemplo n.º 18
0
def _implicit_same_document(t, sig):
    if config.same_document_is_root:
        return root_elt(copy.deepcopy(t))
    else:
        return copy.deepcopy(sig.getparent())
Exemplo n.º 19
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