From d4d56a6705c870901bc73882e4804367f7c9c91a Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Sun, 1 Dec 2024 20:16:54 +0200 Subject: [PATCH] ipalib/x509: support PyCA 44.0 PyCA made x509.Certificate class concrete, it cannot be extended anymore by Python code. The intent is to use helper functions to instantiate certificate objects and never create them directly. FreeIPA wraps PyCA's x509.Certificate class and provides own shim on top of it. In most cases we load the certificate content via the helper functions and don't really need to derive from the certificate class. Move IPACertificate to be a normal Python object class that stores x509.Certificate internally. The only place where this breaks is when IPACertificate object needs to be passed to a code that expects x509.Certificate (Dogtag PKI). In such cases, expose the underlying certificate instance via IPACertificate.cert property. Fixes: https://pagure.io/freeipa/issue/9708 Signed-off-by: Alexander Bokovoy Reviewed-By: Florence Blanc-Renaud --- ipalib/ipajson.py | 4 ++-- ipalib/x509.py | 10 +++++++++- ipapython/ipaldap.py | 15 +++++++-------- ipaserver/plugins/dogtag.py | 3 ++- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/ipalib/ipajson.py b/ipalib/ipajson.py index 5551d12e5fec7e458fa6fe85560664b2fd897337..fd99c8219c722c52321336f28ff27e1573e906c7 100644 --- a/ipalib/ipajson.py +++ b/ipalib/ipajson.py @@ -9,7 +9,7 @@ from decimal import Decimal import json import six from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT -from ipalib import capabilities +from ipalib import capabilities, x509 from ipalib.x509 import Encoding as x509_Encoding from ipapython.dn import DN from ipapython.dnsutil import DNSName @@ -72,7 +72,7 @@ class _JSONPrimer(dict): list: self._enc_list, tuple: self._enc_list, dict: self._enc_dict, - crypto_x509.Certificate: self._enc_certificate, + x509.IPACertificate: self._enc_certificate, crypto_x509.CertificateSigningRequest: self._enc_certificate, }) diff --git a/ipalib/x509.py b/ipalib/x509.py index fd08238962b2b5e9cd056fb13c0a81ee8f31b092..6780bead00b50efdf03c62ce717572eeb9df2e5f 100644 --- a/ipalib/x509.py +++ b/ipalib/x509.py @@ -88,7 +88,7 @@ SAN_UPN = '1.3.6.1.4.1.311.20.2.3' SAN_KRB5PRINCIPALNAME = '1.3.6.1.5.2.2' -class IPACertificate(crypto_x509.Certificate): +class IPACertificate: """ A proxy class wrapping a python-cryptography certificate representation for IPA purposes @@ -205,6 +205,10 @@ class IPACertificate(crypto_x509.Certificate): """ return self._cert.fingerprint(algorithm) + @property + def cert(self): + return self._cert + @property def serial_number(self): return self._cert.serial_number @@ -457,6 +461,8 @@ def load_pem_x509_certificate(data): :returns: a ``IPACertificate`` object. :raises: ``ValueError`` if unable to load the certificate. """ + if isinstance(data, IPACertificate): + return data return IPACertificate( crypto_x509.load_pem_x509_certificate(data, backend=default_backend()) ) @@ -469,6 +475,8 @@ def load_der_x509_certificate(data): :returns: a ``IPACertificate`` object. :raises: ``ValueError`` if unable to load the certificate. """ + if isinstance(data, IPACertificate): + return data return IPACertificate( crypto_x509.load_der_x509_certificate(data, backend=default_backend()) ) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 1888e40916aa6e641542f08fb30ff2b0d4b850b1..5bb81c1bc844fce9b14251d3702e09099d85cdb5 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -33,7 +33,6 @@ import warnings from collections import OrderedDict -from cryptography import x509 as crypto_x509 from cryptography.hazmat.primitives import serialization import ldap @@ -748,10 +747,10 @@ class LDAPClient: 'dnszoneidnsname': DNSName, 'krbcanonicalname': Principal, 'krbprincipalname': Principal, - 'usercertificate': crypto_x509.Certificate, - 'usercertificate;binary': crypto_x509.Certificate, - 'cACertificate': crypto_x509.Certificate, - 'cACertificate;binary': crypto_x509.Certificate, + 'usercertificate': x509.IPACertificate, + 'usercertificate;binary': x509.IPACertificate, + 'cACertificate': x509.IPACertificate, + 'cACertificate;binary': x509.IPACertificate, 'nsds5replicalastupdatestart': unicode, 'nsds5replicalastupdateend': unicode, 'nsds5replicalastinitstart': unicode, @@ -1000,7 +999,7 @@ class LDAPClient: return dct elif isinstance(val, datetime): return val.strftime(LDAP_GENERALIZED_TIME_FORMAT).encode('utf-8') - elif isinstance(val, crypto_x509.Certificate): + elif isinstance(val, x509.IPACertificate): return val.public_bytes(x509.Encoding.DER) elif val is None: return None @@ -1027,7 +1026,7 @@ class LDAPClient: return DNSName.from_text(val.decode('utf-8')) elif target_type in (DN, Principal): return target_type(val.decode('utf-8')) - elif target_type is crypto_x509.Certificate: + elif target_type is x509.IPACertificate: return x509.load_der_x509_certificate(val) else: return target_type(val) @@ -1381,7 +1380,7 @@ class LDAPClient: ] return cls.combine_filters(flts, rules) elif value is not None: - if isinstance(value, crypto_x509.Certificate): + if isinstance(value, x509.IPACertificate): value = value.public_bytes(serialization.Encoding.DER) if isinstance(value, bytes): value = binascii.hexlify(value).decode('ascii') diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 78afb279795ecf74f296cbbb8724505075a6e4a9..ee6d0e347d640a2664e38ba64785c3d8af54bbad 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -1581,7 +1581,8 @@ class kra(Backend): crypto = cryptoutil.CryptographyCryptoProvider( transport_cert_nick="ra_agent", - transport_cert=x509.load_certificate_from_file(paths.RA_AGENT_PEM) + transport_cert=x509.load_certificate_from_file( + paths.RA_AGENT_PEM).cert ) # TODO: obtain KRA host & port from IPA service list or point to KRA load balancer -- 2.47.1