Compare commits
7 Commits
Author | SHA256 | Date | |
---|---|---|---|
abec67dc18 | |||
801a6fab2d | |||
fca54dde28 | |||
410af15d4e | |||
20c2ecea29 | |||
53c425958f | |||
7d756f84ad |
@@ -1,3 +1,33 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Apr 8 07:29:45 UTC 2025 - Ben Greiner <code@bnavigator.de>
|
||||||
|
|
||||||
|
- Fix dependencies gh#IdentityPython/pysaml2#984
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Feb 28 04:15:23 UTC 2025 - Nico Krapp <nico.krapp@suse.com>
|
||||||
|
|
||||||
|
- Update to 7.5.2
|
||||||
|
* Include the XSD of the XML Encryption Syntax and Processing
|
||||||
|
Version 1.1 to the schema validator
|
||||||
|
- Update to 7.5.1
|
||||||
|
* deps: restrict pyOpenSSL up to v24.2.1 until it is replaced
|
||||||
|
* deps: update dependncies for the lockfile and examples
|
||||||
|
- add use-cryptography.patch to fix tests
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Oct 29 08:15:43 UTC 2024 - Dirk Müller <dmueller@suse.com>
|
||||||
|
|
||||||
|
- update to 7.5.0:
|
||||||
|
* Fix missing requested attributes from the ACS
|
||||||
|
* Add support for errorURL to be exposed in metadata for IdP
|
||||||
|
* Update logged message when the signature validation on the
|
||||||
|
assertion fails
|
||||||
|
* Replace imp with importlib
|
||||||
|
* deps: restrict xmlschema version
|
||||||
|
* deps: remove utility from packaging
|
||||||
|
* examples: update code and README to align with latest code
|
||||||
|
* docs: update readme with info about xmlsec1 compatibility
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Mon Jan 29 13:54:33 UTC 2024 - Petr Gajdos <pgajdos@suse.com>
|
Mon Jan 29 13:54:33 UTC 2024 - Petr Gajdos <pgajdos@suse.com>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# spec file for package python-pysaml2
|
# spec file for package python-pysaml2
|
||||||
#
|
#
|
||||||
# Copyright (c) 2024 SUSE LLC
|
# Copyright (c) 2025 SUSE LLC
|
||||||
#
|
#
|
||||||
# All modifications and additions to the file contributed by third parties
|
# All modifications and additions to the file contributed by third parties
|
||||||
# remain the property of their copyright owners, unless otherwise agreed
|
# remain the property of their copyright owners, unless otherwise agreed
|
||||||
@@ -19,29 +19,28 @@
|
|||||||
%global modname pysaml2
|
%global modname pysaml2
|
||||||
%{?sle15_python_module_pythons}
|
%{?sle15_python_module_pythons}
|
||||||
Name: python-pysaml2
|
Name: python-pysaml2
|
||||||
Version: 7.4.2
|
Version: 7.5.2
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: Python implementation of SAML Version 2 to be used in a WSGI environment
|
Summary: Python implementation of SAML Version 2 to be used in a WSGI environment
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
URL: https://github.com/IdentityPython/pysaml2
|
URL: https://github.com/IdentityPython/pysaml2
|
||||||
Source: https://github.com/IdentityPython/pysaml2/archive/v%{version}.tar.gz
|
Source: https://github.com/IdentityPython/pysaml2/archive/v%{version}.tar.gz
|
||||||
|
# PATCH-FIX-UPSTREAM use-cryptography.patch https://github.com/IdentityPython/pysaml2/issues/879
|
||||||
|
Patch0: use-cryptography.patch
|
||||||
BuildRequires: %{python_module Paste}
|
BuildRequires: %{python_module Paste}
|
||||||
BuildRequires: %{python_module cryptography >= 3.1}
|
BuildRequires: %{python_module base >= 3.9}
|
||||||
|
BuildRequires: %{python_module cryptography >= 40.0}
|
||||||
BuildRequires: %{python_module dbm}
|
BuildRequires: %{python_module dbm}
|
||||||
BuildRequires: %{python_module defusedxml}
|
BuildRequires: %{python_module defusedxml}
|
||||||
BuildRequires: %{python_module importlib-resources}
|
|
||||||
BuildRequires: %{python_module pip}
|
BuildRequires: %{python_module pip}
|
||||||
BuildRequires: %{python_module poetry-core}
|
BuildRequires: %{python_module poetry-core}
|
||||||
BuildRequires: %{python_module pyOpenSSL}
|
|
||||||
BuildRequires: %{python_module pymongo >= 3.5}
|
BuildRequires: %{python_module pymongo >= 3.5}
|
||||||
BuildRequires: %{python_module pytest}
|
BuildRequires: %{python_module pytest}
|
||||||
BuildRequires: %{python_module python-dateutil}
|
BuildRequires: %{python_module python-dateutil}
|
||||||
BuildRequires: %{python_module pytz}
|
BuildRequires: %{python_module pytz}
|
||||||
BuildRequires: %{python_module repoze.who}
|
|
||||||
BuildRequires: %{python_module requests >= 1.0.0}
|
BuildRequires: %{python_module requests >= 1.0.0}
|
||||||
BuildRequires: %{python_module responses}
|
BuildRequires: %{python_module responses}
|
||||||
BuildRequires: %{python_module setuptools}
|
BuildRequires: %{python_module xmlschema >= 2}
|
||||||
BuildRequires: %{python_module xmlschema >= 1.2.1}
|
|
||||||
BuildRequires: %{python_module zope.interface}
|
BuildRequires: %{python_module zope.interface}
|
||||||
BuildRequires: fdupes
|
BuildRequires: fdupes
|
||||||
# This is needed as xmlsec itself does not pull any backend by default
|
# This is needed as xmlsec itself does not pull any backend by default
|
||||||
@@ -53,12 +52,10 @@ BuildRequires: xmlsec1
|
|||||||
Requires: python-Paste
|
Requires: python-Paste
|
||||||
Requires: python-cryptography >= 3.1
|
Requires: python-cryptography >= 3.1
|
||||||
Requires: python-defusedxml
|
Requires: python-defusedxml
|
||||||
Requires: python-importlib-resources
|
|
||||||
Requires: python-pyOpenSSL
|
Requires: python-pyOpenSSL
|
||||||
Requires: python-pymongo >= 3.5
|
Requires: python-pymongo >= 3.5
|
||||||
Requires: python-python-dateutil
|
Requires: python-python-dateutil
|
||||||
Requires: python-pytz
|
Requires: python-pytz
|
||||||
Requires: python-repoze.who
|
|
||||||
Requires: python-requests >= 1.0.0
|
Requires: python-requests >= 1.0.0
|
||||||
Requires: python-xmlschema >= 1.2.1
|
Requires: python-xmlschema >= 1.2.1
|
||||||
Requires: python-zope.interface
|
Requires: python-zope.interface
|
||||||
@@ -89,7 +86,7 @@ rm -f tests/test_30_mdstore*.py
|
|||||||
for exec in make_metadata parse_xsd2 mdexport merge_metadata ; do
|
for exec in make_metadata parse_xsd2 mdexport merge_metadata ; do
|
||||||
%python_clone -a %{buildroot}%{_bindir}/$exec
|
%python_clone -a %{buildroot}%{_bindir}/$exec
|
||||||
done
|
done
|
||||||
%python_expand rm -r %{buildroot}%{$python_sitelib}/{saml2test,utility}
|
%python_expand rm -r %{buildroot}%{$python_sitelib}/saml2test
|
||||||
%python_expand %fdupes %{buildroot}%{$python_sitelib}
|
%python_expand %fdupes %{buildroot}%{$python_sitelib}
|
||||||
|
|
||||||
%check
|
%check
|
||||||
@@ -97,10 +94,11 @@ done
|
|||||||
sed -i 's:import mock:from unittest import mock:' tests/test_41_response.py
|
sed -i 's:import mock:from unittest import mock:' tests/test_41_response.py
|
||||||
sed -i 's:mock.mock:unittest.mock:' tests/test_52_default_sign_alg.py
|
sed -i 's:mock.mock:unittest.mock:' tests/test_52_default_sign_alg.py
|
||||||
# Excluded tests for i586 gh#IdentityPython/pysaml2#682 and gh#IdentityPython/pysaml2#759
|
# Excluded tests for i586 gh#IdentityPython/pysaml2#682 and gh#IdentityPython/pysaml2#759
|
||||||
|
# Exclude broken namespace test (https://github.com/IdentityPython/pysaml2/issues/921)
|
||||||
%ifarch %{ix86}
|
%ifarch %{ix86}
|
||||||
%pytest -k "not (test_assertion_consumer_service or test_swamid_sp or test_swamid_idp or test_other_response or test_mta or test_unknown_subject or test_filter_ava_registration_authority_1)" tests
|
%pytest -k "not (test_namespace_processing or test_assertion_consumer_service or test_swamid_sp or test_swamid_idp or test_other_response or test_mta or test_unknown_subject or test_filter_ava_registration_authority_1)" tests
|
||||||
%else
|
%else
|
||||||
%pytest tests
|
%pytest -k "not test_namespace_processing" tests
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%post
|
%post
|
||||||
@@ -117,6 +115,6 @@ sed -i 's:mock.mock:unittest.mock:' tests/test_52_default_sign_alg.py
|
|||||||
%python_alternative %{_bindir}/mdexport
|
%python_alternative %{_bindir}/mdexport
|
||||||
%python_alternative %{_bindir}/merge_metadata
|
%python_alternative %{_bindir}/merge_metadata
|
||||||
%{python_sitelib}/saml2
|
%{python_sitelib}/saml2
|
||||||
%{python_sitelib}/pysaml2-%{version}*-info
|
%{python_sitelib}/pysaml2-%{version}.dist-info
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
342
use-cryptography.patch
Normal file
342
use-cryptography.patch
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
From 930a652a240c8cd1489429a7d70cf5fa7ef1606a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Patrick Rauscher <prauscher@prauscher.de>
|
||||||
|
Date: Wed, 12 Feb 2025 23:29:34 +0100
|
||||||
|
Subject: [PATCH] replace pyopenssl with cryptography
|
||||||
|
|
||||||
|
---
|
||||||
|
pyproject.toml | 3 +-
|
||||||
|
src/saml2/cert.py | 178 ++++++++++++++++++++++++--------------------
|
||||||
|
src/saml2/sigver.py | 12 +--
|
||||||
|
3 files changed, 105 insertions(+), 88 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/pyproject.toml b/pyproject.toml
|
||||||
|
index 985692043..8a7cd9185 100644
|
||||||
|
--- a/pyproject.toml
|
||||||
|
+++ b/pyproject.toml
|
||||||
|
@@ -37,12 +37,11 @@ parse_xsd2 = "saml2.tools.parse_xsd2:main"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.9"
|
||||||
|
-cryptography = ">=3.1"
|
||||||
|
+cryptography = ">=40.0"
|
||||||
|
defusedxml = "*"
|
||||||
|
importlib-metadata = {version = ">=1.7.0", python = "<3.8"}
|
||||||
|
importlib-resources = {python = "<3.9", version = "*"}
|
||||||
|
paste = {optional = true, version = "*"}
|
||||||
|
-pyopenssl = "<24.3.0"
|
||||||
|
python-dateutil = "*"
|
||||||
|
pytz = "*"
|
||||||
|
"repoze.who" = {optional = true, version = "*"}
|
||||||
|
diff --git a/src/saml2/cert.py b/src/saml2/cert.py
|
||||||
|
index c5f626601..1759b9b24 100644
|
||||||
|
--- a/src/saml2/cert.py
|
||||||
|
+++ b/src/saml2/cert.py
|
||||||
|
@@ -5,7 +5,11 @@
|
||||||
|
from os import remove
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
|
-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 pytz
|
||||||
|
|
||||||
|
@@ -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 = pytz.UTC.localize(datetime.datetime.utcnow())
|
||||||
|
- 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 f3af1ec99..98d11b1d1 100644
|
||||||
|
--- a/src/saml2/sigver.py
|
||||||
|
+++ b/src/saml2/sigver.py
|
||||||
|
@@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
|
-from OpenSSL import crypto
|
||||||
|
+from cryptography import x509
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
from saml2 import ExtensionElement
|
||||||
|
@@ -383,14 +383,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 = pytz.UTC.localize(datetime.datetime.utcnow())
|
||||||
|
- 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
|
||||||
|
|
||||||
|
|
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:dfeaa53854467cdcdae1b0fb2c76a3610f8b179dca8176757d245db7fa7ad806
|
|
||||||
size 6059879
|
|
3
v7.5.2.tar.gz
Normal file
3
v7.5.2.tar.gz
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f0bed0419258f6c8540b9d3eace7815206c337f61c91fb253b53e39eafa707a4
|
||||||
|
size 6070199
|
Reference in New Issue
Block a user