Skip to content

Commit 49fdf11

Browse files
authored
bpo-36076: Add SNI support to ssl.get_server_certificate. (GH-16820)
Many servers in the cloud environment require SNI to be used during the SSL/TLS handshake, therefore it is not possible to fetch their certificates using the ssl.get_server_certificate interface. This change adds an additional optional hostname argument that can be used to set the SNI. Note that it is intentionally a separate argument instead of using the host part of the addr tuple, because one might want to explicitly fetch the default certificate or fetch a certificate from a specific IP address with the specified SNI hostname. A separate argument also works better for backwards compatibility. Automerge-Triggered-By: GH:tiran
1 parent 2798f24 commit 49fdf11

File tree

4 files changed

+28
-2
lines changed

4 files changed

+28
-2
lines changed

Lib/ssl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1475,7 +1475,7 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None):
14751475
cert_reqs=cert_reqs,
14761476
cafile=ca_certs)
14771477
with create_connection(addr) as sock:
1478-
with context.wrap_socket(sock) as sslsock:
1478+
with context.wrap_socket(sock, server_hostname=host) as sslsock:
14791479
dercert = sslsock.getpeercert(True)
14801480
return DER_cert_to_PEM_cert(dercert)
14811481

Lib/test/test_ssl.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1962,7 +1962,9 @@ class SimpleBackgroundTests(unittest.TestCase):
19621962
"""Tests that connect to a simple server running in the background"""
19631963

19641964
def setUp(self):
1965-
server = ThreadedEchoServer(SIGNED_CERTFILE)
1965+
self.server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
1966+
self.server_context.load_cert_chain(SIGNED_CERTFILE)
1967+
server = ThreadedEchoServer(context=self.server_context)
19661968
self.server_addr = (HOST, server.port)
19671969
server.__enter__()
19681970
self.addCleanup(server.__exit__, None, None, None)
@@ -2143,6 +2145,28 @@ def test_non_blocking_handshake(self):
21432145
def test_get_server_certificate(self):
21442146
_test_get_server_certificate(self, *self.server_addr, cert=SIGNING_CA)
21452147

2148+
@needs_sni
2149+
def test_get_server_certificate_sni(self):
2150+
host, port = self.server_addr
2151+
server_names = []
2152+
2153+
# We store servername_cb arguments to make sure they match the host
2154+
def servername_cb(ssl_sock, server_name, initial_context):
2155+
server_names.append(server_name)
2156+
self.server_context.set_servername_callback(servername_cb)
2157+
2158+
pem = ssl.get_server_certificate((host, port))
2159+
if not pem:
2160+
self.fail("No server certificate on %s:%s!" % (host, port))
2161+
2162+
pem = ssl.get_server_certificate((host, port), ca_certs=SIGNING_CA)
2163+
if not pem:
2164+
self.fail("No server certificate on %s:%s!" % (host, port))
2165+
if support.verbose:
2166+
sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port, pem))
2167+
2168+
self.assertEqual(server_names, [host, host])
2169+
21462170
def test_get_server_certificate_fail(self):
21472171
# Connection failure crashes ThreadedEchoServer, so run this in an
21482172
# independent test method

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,6 +1803,7 @@ Michael Urman
18031803
Hector Urtubia
18041804
Elizabeth Uselton
18051805
Lukas Vacek
1806+
Juho Vähä-Herttua
18061807
Ville Vainio
18071808
Yann Vaginay
18081809
Andi Vajda
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added SNI support to :func:`ssl.get_server_certificate`.

0 commit comments

Comments
 (0)