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 :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 encrypts and decrypts the data going over the socket with SSL. It supports
additional methods such as :meth:`getpeercert`, which retrieves the additional methods such as :meth:`getpeercert`, which retrieves the
certificate of the other side of the connection, and :meth:`cipher`, which certificate of the other side of the connection, :meth:`cipher`, which
retrieves the cipher being used for the secure connection. 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 For more sophisticated applications, the :class:`ssl.SSLContext` class
helps manage settings and certificates, which can then be inherited 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 .. versionchanged:: 3.9
IPv6 address strings no longer have a trailing new line. 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() .. method:: SSLSocket.cipher()
Returns a three-value tuple containing the name of the cipher being used, the 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 Due to the early negotiation phase of the TLS connection, only limited
methods and attributes are usable like methods and attributes are usable like
:meth:`SSLSocket.selected_alpn_protocol` and :attr:`SSLSocket.context`. :meth:`SSLSocket.selected_alpn_protocol` and :attr:`SSLSocket.context`.
The :meth:`SSLSocket.getpeercert`, The :meth:`SSLSocket.getpeercert`, :meth:`SSLSocket.get_verified_chain`,
:meth:`SSLSocket.cipher` and :meth:`SSLSocket.compression` methods require that :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 the TLS connection has progressed beyond the TLS Client Hello and therefore
will not return meaningful values nor can they be called safely. will not return meaningful values nor can they be called safely.
@ -2414,6 +2433,8 @@ provided.
- :meth:`~SSLSocket.read` - :meth:`~SSLSocket.read`
- :meth:`~SSLSocket.write` - :meth:`~SSLSocket.write`
- :meth:`~SSLSocket.getpeercert` - :meth:`~SSLSocket.getpeercert`
- :meth:`~SSLSocket.get_verified_chain`
- :meth:`~SSLSocket.get_unverified_chain`
- :meth:`~SSLSocket.selected_alpn_protocol` - :meth:`~SSLSocket.selected_alpn_protocol`
- :meth:`~SSLSocket.selected_npn_protocol` - :meth:`~SSLSocket.selected_npn_protocol`
- :meth:`~SSLSocket.cipher` - :meth:`~SSLSocket.cipher`

View file

@ -876,6 +876,31 @@ class SSLObject:
""" """
return self._sslobj.getpeercert(binary_form) 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): def selected_npn_protocol(self):
"""Return the currently selected NPN protocol as a string, or ``None`` """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 if a next protocol was not negotiated or if NPN is not supported by one
@ -1129,6 +1154,14 @@ class SSLSocket(socket):
self._check_connected() self._check_connected()
return self._sslobj.getpeercert(binary_form) 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 @_sslcopydoc
def selected_npn_protocol(self): def selected_npn_protocol(self):
self._checkClosed() 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.