Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
six
defusedxml
lxml
pyconfig
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
},
zip_safe=False,
install_requires=install_requires,
requires=install_requires,
extras_require=extras_require,
entry_points={
'console_scripts': ['xmlsign=xmlsec.tools:sign_cmd','xmlverify=xmlsec.tools:verify_cmd']
Expand Down
40 changes: 21 additions & 19 deletions src/xmlsec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
DS = ElementMaker(namespace=NS['ds'], nsmap=NSDefault)


log = logging.getLogger('xmlsec')

class Config(object):
"""
This class holds a set of configuration parameters (using pyconfig) for pyXMLSecurity:
Expand Down Expand Up @@ -92,7 +94,7 @@ def _signed_value(data, key_size, do_pad, hash_alg): # TODO Do proper asn1 CMS

def _get_by_id(t, id_v):
for id_a in config.id_attributes:
logging.debug("Looking for #%s using id attribute '%s'" % (id_v, id_a))
log.debug("Looking for #%s using id attribute '%s'" % (id_v, id_a))
elts = t.xpath("//*[@%s='%s']" % (id_a, id_v))
if elts is not None and len(elts) > 0:
return elts[0]
Expand Down Expand Up @@ -176,21 +178,21 @@ def _process_references(t, sig, verify_mode=True, sig_path=".//{%s}Signature" %
fd.write(obj)

hash_alg = _ref_digest(ref)
logging.debug("using hash algorithm %s" % hash_alg)
log.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))
log.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))
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:
logging.error("not returning ref %s - digest mismatch" % uri)
log.error("not returning ref %s - digest mismatch" % uri)
else: # signing - lets store the digest
logging.debug("replacing digest in %s" % etree.tostring(dv))
log.debug("replacing digest in %s" % etree.tostring(dv))
dv.text = digest


Expand Down Expand Up @@ -306,12 +308,12 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F
if not sv:
raise XMLSigException("No SignatureValue")

logging.debug("SignatureValue: {!s}".format(sv))
log.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))
log.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)))
log.debug("Found signedinfo {!s}".format(etree.tostring(si)))
cm_alg = _cm_alg(si)
try:
sig_digest_alg = _sig_alg(si)
Expand All @@ -321,12 +323,12 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F
refmap = _process_references(t, sig, verify_mode=True, sig_path=sig_path, drop_signature=drop_signature)
for ref,obj in refmap.items():

logging.debug("transform %s on %s" % (cm_alg, etree.tostring(si)))
log.debug("transform %s on %s" % (cm_alg, etree.tostring(si)))
sic = _transform(cm_alg, si)
logging.debug("SignedInfo C14N: %s" % sic)
log.debug("SignedInfo C14N: %s" % sic)
if this_cert.do_digest:
digest = xmlsec.crypto._digest(sic, sig_digest_alg)
logging.debug("SignedInfo digest: %s" % digest)
log.debug("SignedInfo digest: %s" % digest)
b_digest = b64d(digest)
actual = _signed_value(b_digest, this_cert.keysize, True, sig_digest_alg)
else:
Expand All @@ -336,7 +338,7 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F
raise XMLSigException("Failed to validate {!s} using sig digest {!s} and cm {!s}".format(etree.tostring(sig), sig_digest_alg, cm_alg))
validated.append(obj)
except XMLSigException as ex:
logging.error(ex)
log.error(ex)

if not validated:
raise XMLSigException("No valid ds:Signature elements found")
Expand Down Expand Up @@ -437,7 +439,7 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path
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))
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:
Expand All @@ -451,22 +453,22 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path
fd.write(etree_to_string(root_elt(t)))

for sig in templates:
logging.debug("processing sig template: %s" % etree.tostring(sig))
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
logging.debug("transform %s on %s" % (cm_alg, etree.tostring(si)))
log.debug("transform %s on %s" % (cm_alg, etree.tostring(si)))
sic = _transform(cm_alg, si)
logging.debug("SignedInfo C14N: %s" % sic)
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)
logging.debug("SignedInfo digest: %s" % digest)
log.debug("SignedInfo digest: %s" % digest)
b_digest = b64d(digest)
tbs = _signed_value(b_digest, private.keysize, private.do_padding, sig_alg)
else:
Expand All @@ -476,7 +478,7 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path
signature = b64e(signed)
if isinstance(signature, six.binary_type):
signature = six.text_type(signature, 'utf-8')
logging.debug("SignatureValue: %s" % signature)
log.debug("SignatureValue: %s" % signature)
sv = sig.find(".//{%s}SignatureValue" % NS['ds'])
if sv is None:
si.addnext(DS.SignatureValue(signature))
Expand Down
21 changes: 15 additions & 6 deletions src/xmlsec/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import logging
import threading
import six
from six.moves import xrange
from xmlsec import constants
from binascii import hexlify
from xmlsec.exceptions import XMLSigException
from xmlsec.utils import unicode_to_bytes
Expand All @@ -12,6 +14,7 @@
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding, utils
from cryptography.x509 import load_pem_x509_certificate, load_der_x509_certificate, Certificate
import base64

if six.PY2:
from UserDict import DictMixin
Expand Down Expand Up @@ -223,16 +226,22 @@ def sign(self, data, hash_alg=None):
import requests
import json
url = '{!s}/rawsign'.format(self._keyspec)
r = requests.post(url, json=dict(mech='RSAPKCS1', data=data.encode("base64")))
if not isinstance(data, six.binary_type):
data = data.encode("utf-8")
data = base64.b64encode(data)
r = requests.post(url, json=dict(mech='RSAPKCS1', data=data))
if r.status_code != requests.codes.ok:
r.raise_for_status()
msg = r.json()
if not 'signed' in msg:
if 'signed' not in msg:
raise ValueError("Missing signed data in response message")
return msg['signed'].decode('base64')
signed_msg = msg['signed']
if not isinstance(signed_msg, six.binary_type):
signed_msg = signed_msg.encode("utf-8")
return base64.b64decode(signed_msg)
except Exception as ex:
from traceback import print_exc
print_exc(ex)
from traceback import format_exc
log.debug(format_exc())
raise XMLSigException(ex)


Expand Down Expand Up @@ -311,7 +320,7 @@ def _cert_fingerprint(cert_pem):
else:
cert = load_der_x509_certificate(base64.standard_b64decode(cert_pem), backend=default_backend())

fingerprint = hexlify(cert.fingerprint(hashes.SHA1())).lower()
fingerprint = hexlify(cert.fingerprint(hashes.SHA1())).lower().decode('ascii')
fingerprint = ":".join([fingerprint[x:x + 2] for x in xrange(0, len(fingerprint), 2)])

return fingerprint, cert
Expand Down
10 changes: 0 additions & 10 deletions src/xmlsec/test/p11_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,16 +238,6 @@ def teardown(self):
self.p11_test_files = []


def _get_all_signatures(t):
res = []
for sig in t.findall(".//{%s}Signature" % xmlsec.NS['ds']):
sv = sig.findtext(".//{%s}SignatureValue" % xmlsec.NS['ds'])
assert sv is not None
# base64-dance to normalize newlines
res.append(sv.decode('base64').encode('base64'))
return res


class TestPKCS11(unittest.TestCase):
def setUp(self):
datadir = pkg_resources.resource_filename(__name__, 'data')
Expand Down