From 013dc466dd473b86c6d72599f3470edcff7272c5 Mon Sep 17 00:00:00 2001 From: Patrick Rauscher Date: Wed, 12 Feb 2025 23:29:34 +0100 Subject: [PATCH 1/5] replace pyopenssl with cryptography --- pyproject.toml | 4 +- src/saml2/cert.py | 178 ++++++++++++++++++++++++-------------------- src/saml2/sigver.py | 24 ++++-- 3 files changed, 117 insertions(+), 89 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 87068b18d..2f0edaad3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,10 +20,8 @@ classifiers = [ ] requires-python = ">= 3.9" dependencies = [ - "cryptography >=3.1", + "cryptography >=40.0", "defusedxml", - "pyopenssl <24.3.0", - "python-dateutil", "requests >=2.0.0,<3.0.0", # ^2 means compatible with 2.x "xmlschema >=2.0.0,<3.0.0" ] diff --git a/src/saml2/cert.py b/src/saml2/cert.py index e90651e44..d6550feac 100644 --- a/src/saml2/cert.py +++ b/src/saml2/cert.py @@ -6,7 +6,11 @@ from datetime import datetime from datetime import timezone -from OpenSSL import crypto +from cryptography import x509 +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID import dateutil.parser import saml2.cryptography.pki @@ -36,7 +40,6 @@ def create_certificate( valid_to=315360000, sn=1, key_length=1024, - hash_alg="sha256", write_to_file=False, cert_dir="", cipher_passphrase=None, @@ -87,8 +90,6 @@ def create_certificate( is 1. :param key_length: Length of the key to be generated. Defaults to 1024. - :param hash_alg: Hash algorithm to use for the key. Default - is sha256. :param write_to_file: True if you want to write the certificate to a file. The method will then return a tuple with path to certificate file and @@ -131,49 +132,68 @@ def create_certificate( k_f = join(cert_dir, key_file) # create a key pair - k = crypto.PKey() - k.generate_key(crypto.TYPE_RSA, key_length) + k = rsa.generate_private_key( + public_exponent=65537, + key_size=key_length, + ) # create a self-signed cert - cert = crypto.X509() + builder = x509.CertificateBuilder() if request: - cert = crypto.X509Req() + builder = x509.CertificateSigningRequestBuilder() if len(cert_info["country_code"]) != 2: raise WrongInput("Country code must be two letters!") - cert.get_subject().C = cert_info["country_code"] - cert.get_subject().ST = cert_info["state"] - cert.get_subject().L = cert_info["city"] - cert.get_subject().O = cert_info["organization"] # noqa: E741 - cert.get_subject().OU = cert_info["organization_unit"] - cert.get_subject().CN = cn + subject_name = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, + cert_info["country_code"]), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, + cert_info["state"]), + x509.NameAttribute(NameOID.LOCALITY_NAME, + cert_info["city"]), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, + cert_info["organization"]), + x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, + cert_info["organization_unit"]), + x509.NameAttribute(NameOID.COMMON_NAME, cn), + ]) + builder = builder.subject_name(subject_name) if not request: - cert.set_serial_number(sn) - cert.gmtime_adj_notBefore(valid_from) # Valid before present time - cert.gmtime_adj_notAfter(valid_to) # 3 650 days - cert.set_issuer(cert.get_subject()) - cert.set_pubkey(k) - cert.sign(k, hash_alg) + now = datetime.datetime.now(datetime.UTC) + builder = builder.serial_number( + sn, + ).not_valid_before( + now + datetime.timedelta(seconds=valid_from), + ).not_valid_after( + now + datetime.timedelta(seconds=valid_to), + ).issuer_name( + subject_name, + ).public_key( + k.public_key(), + ) + cert = builder.sign(k, hashes.SHA256()) try: - if request: - tmp_cert = crypto.dump_certificate_request(crypto.FILETYPE_PEM, cert) - else: - tmp_cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) - tmp_key = None + tmp_cert = cert.public_bytes(serialization.Encoding.PEM) + key_encryption = None if cipher_passphrase is not None: passphrase = cipher_passphrase["passphrase"] if isinstance(cipher_passphrase["passphrase"], str): passphrase = passphrase.encode("utf-8") - tmp_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k, cipher_passphrase["cipher"], passphrase) + key_encryption = serialization.BestAvailableEncryption(passphrase) else: - tmp_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k) + key_encryption = serialization.NoEncryption() + tmp_key = k.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=key_encryption, + ) if write_to_file: - with open(c_f, "w") as fc: - fc.write(tmp_cert.decode("utf-8")) - with open(k_f, "w") as fk: - fk.write(tmp_key.decode("utf-8")) + with open(c_f, "wb") as fc: + fc.write(tmp_cert) + with open(k_f, "wb") as fk: + fk.write(tmp_key) return c_f, k_f return tmp_cert, tmp_key except Exception as ex: @@ -198,7 +218,6 @@ def create_cert_signed_certificate( sign_cert_str, sign_key_str, request_cert_str, - hash_alg="sha256", valid_from=0, valid_to=315360000, sn=1, @@ -222,8 +241,6 @@ def create_cert_signed_certificate( the requested certificate. If you only have a file use the method read_str_from_file to get a string representation. - :param hash_alg: Hash algorithm to use for the key. Default - is sha256. :param valid_from: When the certificate starts to be valid. Amount of seconds from when the certificate is generated. @@ -237,27 +254,29 @@ def create_cert_signed_certificate( :return: String representation of the signed certificate. """ - ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, sign_cert_str) - ca_key = None - if passphrase is not None: - ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, sign_key_str, passphrase) - else: - ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, sign_key_str) - req_cert = crypto.load_certificate_request(crypto.FILETYPE_PEM, request_cert_str) - - cert = crypto.X509() - cert.set_subject(req_cert.get_subject()) - cert.set_serial_number(sn) - cert.gmtime_adj_notBefore(valid_from) - cert.gmtime_adj_notAfter(valid_to) - cert.set_issuer(ca_cert.get_subject()) - cert.set_pubkey(req_cert.get_pubkey()) - cert.sign(ca_key, hash_alg) - - cert_dump = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) - if isinstance(cert_dump, str): - return cert_dump - return cert_dump.decode("utf-8") + if isinstance(sign_cert_str, str): + sign_cert_str = sign_cert_str.encode("utf-8") + ca_cert = x509.load_pem_x509_certificate(sign_cert_str) + ca_key = serialization.load_pem_private_key( + sign_key_str, password=passphrase) + req_cert = x509.load_pem_x509_csr(request_cert_str) + + now = datetime.datetime.now(datetime.UTC) + cert = x509.CertificateBuilder().subject_name( + req_cert.subject, + ).serial_number( + sn, + ).not_valid_before( + now + datetime.timedelta(seconds=valid_from), + ).not_valid_after( + now + datetime.timedelta(seconds=valid_to), + ).issuer_name( + ca_cert.subject, + ).public_key( + req_cert.public_key(), + ).sign(ca_key, hashes.SHA256()) + + return cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") def verify_chain(self, cert_chain_str_list, cert_str): """ @@ -276,13 +295,6 @@ def verify_chain(self, cert_chain_str_list, cert_str): cert_str = tmp_cert_str return (True, "Signed certificate is valid and correctly signed by CA " "certificate.") - def certificate_not_valid_yet(self, cert): - starts_to_be_valid = dateutil.parser.parse(cert.get_notBefore()) - now = datetime.now(timezone.utc) - if starts_to_be_valid < now: - return False - return True - def verify(self, signing_cert_str, cert_str): """ Verifies if a certificate is valid and signed by a given certificate. @@ -303,34 +315,34 @@ def verify(self, signing_cert_str, cert_str): Message = Why the validation failed. """ try: - ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, signing_cert_str) - cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_str) - - if self.certificate_not_valid_yet(ca_cert): + if isinstance(signing_cert_str, str): + signing_cert_str = signing_cert_str.encode("utf-8") + if isinstance(cert_str, str): + cert_str = cert_str.encode("utf-8") + ca_cert = x509.load_pem_x509_certificate(signing_cert_str) + cert = x509.load_pem_x509_certificate(cert_str) + now = datetime.datetime.now(datetime.UTC) + + if ca_cert.not_valid_before_utc >= now: return False, "CA certificate is not valid yet." - if ca_cert.has_expired() == 1: + if ca_cert.not_valid_after_utc < now: return False, "CA certificate is expired." - if cert.has_expired() == 1: + if cert.not_valid_after_utc < now: return False, "The signed certificate is expired." - if self.certificate_not_valid_yet(cert): + if cert.not_valid_before_utc >= now: return False, "The signed certificate is not valid yet." - if ca_cert.get_subject().CN == cert.get_subject().CN: + if ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME) == \ + cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME): return False, ("CN may not be equal for CA certificate and the " "signed certificate.") - cert_algorithm = cert.get_signature_algorithm() - cert_algorithm = cert_algorithm.decode("ascii") - cert_str = cert_str.encode("ascii") - - cert_crypto = saml2.cryptography.pki.load_pem_x509_certificate(cert_str) - try: - crypto.verify(ca_cert, cert_crypto.signature, cert_crypto.tbs_certificate_bytes, cert_algorithm) + cert.verify_directly_issued_by(ca_cert) return True, "Signed certificate is valid and correctly signed by CA certificate." - except crypto.Error as e: + except (ValueError, TypeError, InvalidSignature) as e: return False, f"Certificate is incorrectly signed: {str(e)}" except Exception as e: return False, f"Certificate is not valid for an unknown reason. {str(e)}" @@ -352,8 +364,14 @@ def read_cert_from_file(cert_file, cert_type="pem"): data = fp.read() try: - cert = saml2.cryptography.pki.load_x509_certificate(data, cert_type) - pem_data = saml2.cryptography.pki.get_public_bytes_from_cert(cert) + cert = None + if cert_type == "pem": + cert = x509.load_pem_x509_certificate(data) + elif cert_type == "der": + cert = x509.load_der_x509_certificate(data) + else: + raise ValueError(f"cert-type {cert_type} not supported") + pem_data = cert.public_bytes(serialization.Encoding.PEM).decode("utf-8") except Exception as e: raise CertificateError(e) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index 738ac04b1..b2551290e 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -18,9 +18,21 @@ from urllib import parse from uuid import uuid4 as gen_random_key -from OpenSSL import crypto import dateutil + +# importlib.resources was introduced in python 3.7 +# files API from importlib.resources introduced in python 3.9 +if sys.version_info[:2] >= (3, 9): + from importlib.resources import files as _resource_files +else: + from importlib_resources import files as _resource_files + +from urllib import parse + +from cryptography import x509 +import pytz + from saml2 import ExtensionElement from saml2 import SamlBase from saml2 import SAMLError @@ -373,14 +385,14 @@ def active_cert(key): """ try: cert_str = pem_format(key) - cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_str) + cert = x509.load_pem_x509_certificate(cert_str) except AttributeError: return False - now = datetime.now(timezone.utc) - valid_from = dateutil.parser.parse(cert.get_notBefore()) - valid_to = dateutil.parser.parse(cert.get_notAfter()) - active = not cert.has_expired() and valid_from <= now < valid_to + now = datetime.datetime.now(datetime.UTC) + valid_from = cert.not_valid_before_utc + valid_to = cert.not_valid_after_utc + active = valid_from <= now < valid_to return active From 3713225c930be26a2ef6a1d02b3fc9f9376ae169 Mon Sep 17 00:00:00 2001 From: Patrick Rauscher Date: Fri, 13 Jun 2025 22:29:26 +0200 Subject: [PATCH 2/5] use syntax compatible with python before 3.11 --- src/saml2/cert.py | 6 +++--- src/saml2/sigver.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/saml2/cert.py b/src/saml2/cert.py index d6550feac..b4218ab0d 100644 --- a/src/saml2/cert.py +++ b/src/saml2/cert.py @@ -160,7 +160,7 @@ def create_certificate( ]) builder = builder.subject_name(subject_name) if not request: - now = datetime.datetime.now(datetime.UTC) + now = datetime.datetime.now(datetime.timezone.utc) builder = builder.serial_number( sn, ).not_valid_before( @@ -261,7 +261,7 @@ def create_cert_signed_certificate( sign_key_str, password=passphrase) req_cert = x509.load_pem_x509_csr(request_cert_str) - now = datetime.datetime.now(datetime.UTC) + now = datetime.datetime.now(datetime.timezone.utc) cert = x509.CertificateBuilder().subject_name( req_cert.subject, ).serial_number( @@ -321,7 +321,7 @@ def verify(self, signing_cert_str, cert_str): cert_str = cert_str.encode("utf-8") ca_cert = x509.load_pem_x509_certificate(signing_cert_str) cert = x509.load_pem_x509_certificate(cert_str) - now = datetime.datetime.now(datetime.UTC) + now = datetime.datetime.now(datetime.timezone.utc) if ca_cert.not_valid_before_utc >= now: return False, "CA certificate is not valid yet." diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index b2551290e..e22a00bd0 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -389,7 +389,7 @@ def active_cert(key): except AttributeError: return False - now = datetime.datetime.now(datetime.UTC) + now = datetime.datetime.now(datetime.timezone.utc) valid_from = cert.not_valid_before_utc valid_to = cert.not_valid_after_utc active = valid_from <= now < valid_to From 14824154cbfa685d01777f7161facab0c41763db Mon Sep 17 00:00:00 2001 From: Patrick Rauscher Date: Fri, 11 Jul 2025 20:08:19 +0200 Subject: [PATCH 3/5] update poetry.lock --- poetry.lock | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/poetry.lock b/poetry.lock index fdae3665a..afdfef196 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1331,25 +1331,6 @@ snappy = ["python-snappy"] test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] zstd = ["zstandard"] -[[package]] -name = "pyopenssl" -version = "24.2.1" -description = "Python wrapper module around the OpenSSL library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d"}, - {file = "pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95"}, -] - -[package.dependencies] -cryptography = ">=41.0.5,<44" - -[package.extras] -docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx-rtd-theme"] -test = ["pretend", "pytest (>=3.0.1)", "pytest-rerunfailures"] - [[package]] name = "pytest" version = "8.3.4" @@ -1392,21 +1373,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - [[package]] name = "pyyaml" version = "6.0.2" @@ -1561,7 +1527,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2073,4 +2039,4 @@ s2repoze = ["paste", "repoze.who", "zope.interface"] [metadata] lock-version = "2.1" python-versions = ">= 3.9" -content-hash = "04b0c0e0efb4781e75bd015e82e23592fb454f9bdeb42507a18c9e5a18d30029" +content-hash = "1df2685fba7331d5d33e6a9c3327c93b558ab224300a7fec8103181d0e1f8ccc" From 37edca75fcf0eac6544ae89554d55a4d9f8880eb Mon Sep 17 00:00:00 2001 From: Patrick Rauscher Date: Fri, 11 Jul 2025 20:08:48 +0200 Subject: [PATCH 4/5] remove now unused types-pyopenssl --- poetry.lock | 23 ++++------------------- pyproject.toml | 1 - 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/poetry.lock b/poetry.lock index afdfef196..bfa507af0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -128,7 +128,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["main"] markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, @@ -429,7 +429,7 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -1217,7 +1217,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["main"] markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, @@ -1787,21 +1787,6 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] -[[package]] -name = "types-pyopenssl" -version = "23.3.0.20240106" -description = "Typing stubs for pyOpenSSL" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "types-pyOpenSSL-23.3.0.20240106.tar.gz", hash = "sha256:3d6f3462bec0c260caadf93fbb377225c126661b779c7d9ab99b6dad5ca10db9"}, - {file = "types_pyOpenSSL-23.3.0.20240106-py3-none-any.whl", hash = "sha256:47a7eedbd18b7bcad17efebf1c53416148f5a173918a6d75027e75e32fe039ae"}, -] - -[package.dependencies] -cryptography = ">=35.0.0" - [[package]] name = "types-python-dateutil" version = "2.9.0.20241206" @@ -2039,4 +2024,4 @@ s2repoze = ["paste", "repoze.who", "zope.interface"] [metadata] lock-version = "2.1" python-versions = ">= 3.9" -content-hash = "1df2685fba7331d5d33e6a9c3327c93b558ab224300a7fec8103181d0e1f8ccc" +content-hash = "3cdd02d61a39689246fd78da04a2361f1bc36838c3afc2b61d13ccf39c1511c0" diff --git a/pyproject.toml b/pyproject.toml index 2f0edaad3..d6df874bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,6 @@ flake8-bugbear = "^22.8.23" flake8-logging-format = "^0.7.5" ipdb = "^0.13.9" mypy = "^1.0.0" -types-pyopenssl = "^23.0.0.3" types-python-dateutil = "^2.8.19.6" types-setuptools = "^67.2.0.1" types-six = "^1.16.21.4" From a9b7b78dadf2f403e83c3f647c79a6eb84f1746b Mon Sep 17 00:00:00 2001 From: Patrick Rauscher Date: Thu, 17 Jul 2025 17:21:06 +0200 Subject: [PATCH 5/5] remove dependency to pytz and dateutil --- poetry.lock | 14 +------------- pyproject.toml | 1 - src/saml2/cert.py | 1 - src/saml2/sigver.py | 3 --- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index bfa507af0..5c8b5e92d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1787,18 +1787,6 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20241206" -description = "Typing stubs for python-dateutil" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, - {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, -] - [[package]] name = "types-requests" version = "2.32.0.20241016" @@ -2024,4 +2012,4 @@ s2repoze = ["paste", "repoze.who", "zope.interface"] [metadata] lock-version = "2.1" python-versions = ">= 3.9" -content-hash = "3cdd02d61a39689246fd78da04a2361f1bc36838c3afc2b61d13ccf39c1511c0" +content-hash = "8c71ef858084b94b15d9712995e66a266e374a81388afe73252d232c37b835e3" diff --git a/pyproject.toml b/pyproject.toml index d6df874bc..76bf161ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,6 @@ flake8-bugbear = "^22.8.23" flake8-logging-format = "^0.7.5" ipdb = "^0.13.9" mypy = "^1.0.0" -types-python-dateutil = "^2.8.19.6" types-setuptools = "^67.2.0.1" types-six = "^1.16.21.4" types-requests = "^2.28.11.12" diff --git a/src/saml2/cert.py b/src/saml2/cert.py index b4218ab0d..fbf9e2abc 100644 --- a/src/saml2/cert.py +++ b/src/saml2/cert.py @@ -11,7 +11,6 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID -import dateutil.parser import saml2.cryptography.pki diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index e22a00bd0..328ef1b54 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -18,8 +18,6 @@ from urllib import parse from uuid import uuid4 as gen_random_key -import dateutil - # importlib.resources was introduced in python 3.7 # files API from importlib.resources introduced in python 3.9 @@ -31,7 +29,6 @@ from urllib import parse from cryptography import x509 -import pytz from saml2 import ExtensionElement from saml2 import SamlBase