gh-109109: Expose retrieving certificate chains in SSL module (#109113)

Adds APIs to get the TLS certificate chains, verified or full unverified, from SSLSocket and SSLObject.

Co-authored-by: Gregory P. Smith [Google LLC] <greg@krypto.org>
This commit is contained in:
Mateusz Nowak 2023-09-20 03:20:54 +02:00 committed by GitHub
parent ddf2e953c2
commit 5a740cd06e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 4 deletions

View file

@ -43,8 +43,10 @@ This module provides a class, :class:`ssl.SSLSocket`, which is derived from the
:class:`socket.socket` type, and provides a socket-like wrapper that also
encrypts and decrypts the data going over the socket with SSL. It supports
additional methods such as :meth:`getpeercert`, which retrieves the
certificate of the other side of the connection, and :meth:`cipher`, which
retrieves the cipher being used for the secure connection.
certificate of the other side of the connection, :meth:`cipher`, which
retrieves the cipher being used for the secure connection or
:meth:`get_verified_chain`, :meth:`get_unverified_chain` which retrieves
certificate chain.
For more sophisticated applications, the :class:`ssl.SSLContext` class
helps manage settings and certificates, which can then be inherited
@ -1210,6 +1212,22 @@ SSL sockets also have the following additional methods and attributes:
.. versionchanged:: 3.9
IPv6 address strings no longer have a trailing new line.
.. method:: SSLSocket.get_verified_chain()
Returns verified certificate chain provided by the other
end of the SSL channel as a list of DER-encoded bytes.
If certificate verification was disabled method acts the same as
:meth:`~SSLSocket.get_unverified_chain`.
.. versionadded:: 3.13
.. method:: SSLSocket.get_unverified_chain()
Returns raw certificate chain provided by the other
end of the SSL channel as a list of DER-encoded bytes.
.. versionadded:: 3.13
.. method:: SSLSocket.cipher()
Returns a three-value tuple containing the name of the cipher being used, the
@ -1656,8 +1674,9 @@ to speed up repeated connections from the same clients.
Due to the early negotiation phase of the TLS connection, only limited
methods and attributes are usable like
:meth:`SSLSocket.selected_alpn_protocol` and :attr:`SSLSocket.context`.
The :meth:`SSLSocket.getpeercert`,
:meth:`SSLSocket.cipher` and :meth:`SSLSocket.compression` methods require that
The :meth:`SSLSocket.getpeercert`, :meth:`SSLSocket.get_verified_chain`,
:meth:`SSLSocket.get_unverified_chain` :meth:`SSLSocket.cipher`
and :meth:`SSLSocket.compression` methods require that
the TLS connection has progressed beyond the TLS Client Hello and therefore
will not return meaningful values nor can they be called safely.
@ -2414,6 +2433,8 @@ provided.
- :meth:`~SSLSocket.read`
- :meth:`~SSLSocket.write`
- :meth:`~SSLSocket.getpeercert`
- :meth:`~SSLSocket.get_verified_chain`
- :meth:`~SSLSocket.get_unverified_chain`
- :meth:`~SSLSocket.selected_alpn_protocol`
- :meth:`~SSLSocket.selected_npn_protocol`
- :meth:`~SSLSocket.cipher`

View file

@ -876,6 +876,31 @@ def getpeercert(self, binary_form=False):
"""
return self._sslobj.getpeercert(binary_form)
def get_verified_chain(self):
"""Returns verified certificate chain provided by the other
end of the SSL channel as a list of DER-encoded bytes.
If certificate verification was disabled method acts the same as
``SSLSocket.get_unverified_chain``.
"""
chain = self._sslobj.get_verified_chain()
if chain is None:
return []
return [cert.public_bytes(_ssl.ENCODING_DER) for cert in chain]
def get_unverified_chain(self):
"""Returns raw certificate chain provided by the other
end of the SSL channel as a list of DER-encoded bytes.
"""
chain = self._sslobj.get_unverified_chain()
if chain is None:
return []
return [cert.public_bytes(_ssl.ENCODING_DER) for cert in chain]
def selected_npn_protocol(self):
"""Return the currently selected NPN protocol as a string, or ``None``
if a next protocol was not negotiated or if NPN is not supported by one
@ -1129,6 +1154,14 @@ def getpeercert(self, binary_form=False):
self._check_connected()
return self._sslobj.getpeercert(binary_form)
@_sslcopydoc
def get_verified_chain(self):
return self._sslobj.get_verified_chain()
@_sslcopydoc
def get_unverified_chain(self):
return self._sslobj.get_unverified_chain()
@_sslcopydoc
def selected_npn_protocol(self):
self._checkClosed()

View file

@ -0,0 +1,5 @@
You can now get the raw TLS certificate chains from TLS connections via
:meth:`ssl.SSLSocket.get_verified_chain` and
:meth:`ssl.SSLSocket.get_unverified_chain` methods.
Contributed by Mateusz Nowak.