Skip to content
Open
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
8 changes: 7 additions & 1 deletion Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@ Certificate handling
same format as used for the *cafile* parameter in
:meth:`SSLContext.load_verify_locations`. The call will attempt to validate the
server certificate against that set of root certificates, and will fail
if the validation attempt fails. A timeout can be specified with the
if the validation attempt fails or the certificate hostname does not match
the server hostname. A timeout can be specified with the
``timeout`` parameter.

.. versionchanged:: 3.3
Expand All @@ -445,6 +446,11 @@ Certificate handling
.. versionchanged:: 3.10
The *timeout* parameter was added.

.. versionchanged:: next
The function now raises :exc:`SSLCertVerificationError` if *ca_certs* is
specified but the certificate hostname does not match the server
hostname.

.. function:: DER_cert_to_PEM_cert(der_cert_bytes)

Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded
Expand Down
4 changes: 3 additions & 1 deletion Lib/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,8 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT,
ca_certs=None, timeout=_GLOBAL_DEFAULT_TIMEOUT):
"""Retrieve the certificate from the server at the specified address,
and return it as a PEM-encoded string.
If 'ca_certs' is specified, validate the server cert against it.
If 'ca_certs' is specified, validate the server cert against it and
check that it matches the hostname.
If 'ssl_version' is specified, use it in the connection attempt.
If 'timeout' is specified, use it in the connection attempt.
"""
Expand All @@ -1650,6 +1651,7 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT,
cert_reqs = CERT_NONE
context = _create_stdlib_context(ssl_version,
cert_reqs=cert_reqs,
check_hostname=ca_certs is not None,
cafile=ca_certs)
with create_connection(addr, timeout=timeout) as sock:
with context.wrap_socket(sock, server_hostname=host) as sslsock:
Expand Down
8 changes: 6 additions & 2 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3112,6 +3112,10 @@ def test_get_server_certificate_fail(self):
# independent test method
_test_get_server_certificate_fail(self, *self.server_addr)

def test_get_server_certificate_fail_by_hostname(self):
_, port = self.server_addr
_test_get_server_certificate_fail(self, socket_helper.HOSTv4, port, SIGNING_CA)

def test_get_server_certificate_timeout(self):
def servername_cb(ssl_sock, server_name, initial_context):
time.sleep(0.2)
Expand Down Expand Up @@ -3364,10 +3368,10 @@ def _test_get_server_certificate(test, host, port, cert=None):
if support.verbose:
sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port ,pem))

def _test_get_server_certificate_fail(test, host, port):
def _test_get_server_certificate_fail(test, host, port, ca_certs=CERTFILE):
with warnings_helper.check_no_resource_warning(test):
try:
pem = ssl.get_server_certificate((host, port), ca_certs=CERTFILE)
pem = ssl.get_server_certificate((host, port), ca_certs=ca_certs)
except ssl.SSLError as x:
#should fail
if support.verbose:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. cve: CVE-2026-19552

:func:`ssl.get_server_certificate` now verifies that the server hostname matches
its certificate when the caller specifies *ca_certs*.
Loading