diff --git a/requirements.txt b/requirements.txt index 4f462efe..715c30c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +idna==2.5 +six defusedxml lxml pyconfig diff --git a/setup.py b/setup.py index 309fd527..58263262 100755 --- a/setup.py +++ b/setup.py @@ -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'] diff --git a/src/xmlsec/__init__.py b/src/xmlsec/__init__.py index 1abc221e..f13d5a67 100644 --- a/src/xmlsec/__init__.py +++ b/src/xmlsec/__init__.py @@ -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: @@ -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] @@ -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 @@ -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) @@ -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: @@ -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") @@ -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: @@ -451,7 +453,7 @@ 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) @@ -459,14 +461,14 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path _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: @@ -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)) diff --git a/src/xmlsec/crypto.py b/src/xmlsec/crypto.py index 5ae9a417..3a08f0c8 100644 --- a/src/xmlsec/crypto.py +++ b/src/xmlsec/crypto.py @@ -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 @@ -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 @@ -229,7 +232,7 @@ def sign(self, data, hash_alg=None): msg = r.json() if not 'signed' in msg: raise ValueError("Missing signed data in response message") - return msg['signed'].decode('base64') + return base64.encodestring(msg['signed']) except Exception as ex: from traceback import print_exc print_exc(ex) @@ -311,7 +314,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 diff --git a/src/xmlsec/test/p11_test.py b/src/xmlsec/test/p11_test.py index 04612f33..ec72a29e 100644 --- a/src/xmlsec/test/p11_test.py +++ b/src/xmlsec/test/p11_test.py @@ -12,6 +12,7 @@ import subprocess import shutil import tempfile +import codecs from lxml import etree @@ -244,7 +245,7 @@ def _get_all_signatures(t): 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')) + res.append(codecs.encode(codecs.decode(sv, 'base64'),'base64')) return res