Skip to content

Commit 1da7426

Browse files
authored
Allow Connection.get_peer_cert_chain to return cryptography certificates (#1353)
1 parent b42e6dc commit 1da7426

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Deprecations:
2121
Changes:
2222
^^^^^^^^
2323

24-
* ``OpenSSL.SSL.Connection.get_certificate`` and ``OpenSSL.SSL.Connection.get_peer_certificate`` now take 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+
* ``OpenSSL.SSL.Connection.get_certificate``, ``OpenSSL.SSL.Connection.get_peer_certificate``, and ``OpenSSL.SSL.Connection.get_peer_cert_chain`` now take an ``as_cryptography`` keyword-argument. When ``True`` is passed then ``cryptography.x509.Certificate`` are returned, instead of ``OpenSSL.crypto.X509``. In the future, passing ``False`` (the default) will be deprecated.
2525

2626

2727
24.2.1 (2024-07-20)

src/OpenSSL/SSL.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2781,17 +2781,57 @@ def _cert_stack_to_list(cert_stack: Any) -> list[X509]:
27812781
result.append(pycert)
27822782
return result
27832783

2784-
def get_peer_cert_chain(self) -> list[X509] | None:
2784+
@staticmethod
2785+
def _cert_stack_to_cryptography_list(
2786+
cert_stack: Any,
2787+
) -> list[x509.Certificate]:
2788+
"""
2789+
Internal helper to convert a STACK_OF(X509) to a list of X509
2790+
instances.
2791+
"""
2792+
result = []
2793+
for i in range(_lib.sk_X509_num(cert_stack)):
2794+
cert = _lib.sk_X509_value(cert_stack, i)
2795+
_openssl_assert(cert != _ffi.NULL)
2796+
res = _lib.X509_up_ref(cert)
2797+
_openssl_assert(res >= 1)
2798+
pycert = X509._from_raw_x509_ptr(cert)
2799+
result.append(pycert.to_cryptography())
2800+
return result
2801+
2802+
@typing.overload
2803+
def get_peer_cert_chain(
2804+
self, *, as_cryptography: typing.Literal[True]
2805+
) -> list[x509.Certificate] | None:
2806+
pass
2807+
2808+
@typing.overload
2809+
def get_peer_cert_chain(
2810+
self, *, as_cryptography: typing.Literal[False] = False
2811+
) -> list[X509] | None:
2812+
pass
2813+
2814+
def get_peer_cert_chain(
2815+
self,
2816+
*,
2817+
as_cryptography: typing.Literal[True] | typing.Literal[False] = False,
2818+
) -> list[X509] | list[x509.Certificate] | None:
27852819
"""
27862820
Retrieve the other side's certificate (if any)
27872821
2822+
:param bool as_cryptography: Controls whether a list of
2823+
``cryptography.x509.Certificate`` or ``OpenSSL.crypto.X509``
2824+
object should be returned.
2825+
27882826
:return: A list of X509 instances giving the peer's certificate chain,
27892827
or None if it does not have one.
27902828
"""
27912829
cert_stack = _lib.SSL_get_peer_cert_chain(self._ssl)
27922830
if cert_stack == _ffi.NULL:
27932831
return None
27942832

2833+
if as_cryptography:
2834+
return self._cert_stack_to_cryptography_list(cert_stack)
27952835
return self._cert_stack_to_list(cert_stack)
27962836

27972837
def get_verified_chain(self) -> list[X509] | None:

tests/test_ssl.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,21 @@ def test_get_peer_cert_chain(self):
26122612
assert "Intermediate Certificate" == chain[1].get_subject().CN
26132613
assert "Authority Certificate" == chain[2].get_subject().CN
26142614

2615+
cryptography_chain = client.get_peer_cert_chain(as_cryptography=True)
2616+
assert len(cryptography_chain) == 3
2617+
assert (
2618+
cryptography_chain[0].subject.rfc4514_string()
2619+
== "CN=Server Certificate"
2620+
)
2621+
assert (
2622+
cryptography_chain[1].subject.rfc4514_string()
2623+
== "CN=Intermediate Certificate"
2624+
)
2625+
assert (
2626+
cryptography_chain[2].subject.rfc4514_string()
2627+
== "CN=Authority Certificate"
2628+
)
2629+
26152630
def test_get_peer_cert_chain_none(self):
26162631
"""
26172632
`Connection.get_peer_cert_chain` returns `None` if the peer sends

0 commit comments

Comments
 (0)