From de8305777dd2c1d8d153b5591b7bfe02451e283e Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 9 Aug 2024 10:32:37 -0400 Subject: [PATCH] Allow Connection.get_certificate to return a cryptography certificate --- CHANGELOG.rst | 2 ++ src/OpenSSL/SSL.py | 31 +++++++++++++++++++++++++++++-- tests/test_ssl.py | 7 +++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e10c9569..443a06c8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -21,6 +21,8 @@ Deprecations: Changes: ^^^^^^^^ +* ``OpenSSL.SSL.Connection.get_certificate`` now takes an ``as_cryptography`` keyword-argument. When ``True`` is passed then a ``cryptography.x509.Certificate`` is returned, instead of an ``OpenSSL.crypto.X509``. In the future, passing ``False`` (the default) will be deprecated. + 24.2.1 (2024-07-20) ------------------- diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index e4d9ce14..a25be26f 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -2696,16 +2696,43 @@ def sock_shutdown(self, *args: Any, **kwargs: Any) -> None: """ return self._socket.shutdown(*args, **kwargs) # type: ignore[return-value, union-attr] - def get_certificate(self) -> X509 | None: + @typing.overload + def get_certificate( + self, *, as_cryptography: typing.Literal[True] + ) -> x509.Certificate | None: + pass + + @typing.overload + def get_certificate( + self, *, as_cryptography: typing.Literal[False] = False + ) -> X509 | None: + pass + + @typing.overload + def get_certificate( + self, *, as_cryptography: bool = False + ) -> X509 | x509.Certificate | None: + pass + + def get_certificate( + self, *, as_cryptography: bool = False + ) -> X509 | x509.Certificate | None: """ Retrieve the local certificate (if any) + :param bool as_cryptography: Controls whether a + ``cryptography.x509.Certificate`` or an ``OpenSSL.crypto.X509`` + object should be returned. + :return: The local certificate """ cert = _lib.SSL_get_certificate(self._ssl) if cert != _ffi.NULL: _lib.X509_up_ref(cert) - return X509._from_raw_x509_ptr(cert) + pycert = X509._from_raw_x509_ptr(cert) + if as_cryptography: + return pycert.to_cryptography() + return pycert return None def get_peer_certificate(self) -> X509 | None: diff --git a/tests/test_ssl.py b/tests/test_ssl.py index fa6f40a8..9be03b02 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -2556,6 +2556,13 @@ def test_get_certificate(self): assert cert is not None assert "Server Certificate" == cert.get_subject().CN + cryptography_cert = client.get_certificate(as_cryptography=True) + assert cryptography_cert is not None + assert ( + cryptography_cert.subject.rfc4514_string() + == "CN=Server Certificate" + ) + def test_get_certificate_none(self): """ `Connection.get_certificate` returns the local certificate.