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
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
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
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
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