Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ Deprecations:
Changes:
^^^^^^^^

- ``OpenSSL.SSL.Connection.get_client_ca_list`` now takes an ``as_cryptography`` keyword-argument. When ``True`` is passed then ``cryptography.x509.Name`` are returned, instead of ``OpenSSL.crypto.X509Name``. In the future, passing ``False`` (the default) will be deprecated.


26.2.0 (2026-05-04)
-------------------

Expand Down
35 changes: 34 additions & 1 deletion src/OpenSSL/SSL.py
Original file line number Diff line number Diff line change
Expand Up @@ -2643,10 +2643,30 @@ def get_cipher_list(self) -> list[str]:
ciphers.append(_ffi.string(result).decode("utf-8"))
return ciphers

def get_client_ca_list(self) -> list[X509Name]:
@typing.overload
def get_client_ca_list(
self, *, as_cryptography: typing.Literal[True]
) -> list[x509.Name]:
pass

@typing.overload
def get_client_ca_list(
self, *, as_cryptography: typing.Literal[False] = False
) -> list[X509Name]:
pass

def get_client_ca_list(
self,
*,
as_cryptography: typing.Literal[True] | typing.Literal[False] = False,
) -> list[X509Name] | list[x509.Name]:
"""
Get CAs whose certificates are suggested for client authentication.

:param bool as_cryptography: Controls whether a list of
``cryptography.x509.Name`` or ``OpenSSL.crypto.X509Name``
objects should be returned.

:return: If this is a server connection, the list of certificate
authorities that will be sent or has been sent to the client, as
controlled by this :class:`Connection`'s :class:`Context`.
Expand All @@ -2661,6 +2681,19 @@ def get_client_ca_list(self) -> list[X509Name]:
# TODO: This is untested.
return []

if as_cryptography:
names = []
for i in range(_lib.sk_X509_NAME_num(ca_names)):
name = _lib.sk_X509_NAME_value(ca_names, i)
result_buffer = _ffi.new("unsigned char**")
encode_result = _lib.i2d_X509_NAME(name, result_buffer)
_openssl_assert(encode_result >= 0)
der = _ffi.buffer(result_buffer[0], encode_result)[:]
_lib.OPENSSL_free(result_buffer[0])

names.append(x509.Name.from_bytes(der))
return names

result = []
for i in range(_lib.sk_X509_NAME_num(ca_names)):
name = _lib.sk_X509_NAME_value(ca_names, i)
Expand Down
20 changes: 20 additions & 0 deletions tests/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4474,6 +4474,26 @@ def single_ca(ctx: Context) -> list[X509Name]:

self._check_client_ca_list(single_ca)

def test_get_client_ca_list_as_cryptography(self) -> None:
"""
`Connection.get_client_ca_list` returns a list of
`cryptography.x509.Name` when called with ``as_cryptography=True``.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
expected = [cacert.to_cryptography().subject]

server_ctx = self._create_server_context()
server_ctx.set_client_ca_list([cacert.get_subject()])

server = self._server(None, server_ctx)
client = self._client(None)

assert client.get_client_ca_list(as_cryptography=True) == []
assert server.get_client_ca_list(as_cryptography=True) == expected
interact_in_memory(client, server)
assert client.get_client_ca_list(as_cryptography=True) == expected
assert server.get_client_ca_list(as_cryptography=True) == expected

def test_set_multiple_ca_list(self) -> None:
"""
If passed a list containing multiple X509Name objects,
Expand Down
Loading