Skip to content

Commit 8ef358d

Browse files
matiuszkagpshead
andauthored
gh-118658: Return consistent types from get_un/verified_chain in SSLObject and SSLSocket (#118669)
Co-authored-by: Gregory P. Smith [Google LLC] <[email protected]>
1 parent c13e7d9 commit 8ef358d

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

Lib/ssl.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,11 +1164,21 @@ def getpeercert(self, binary_form=False):
11641164

11651165
@_sslcopydoc
11661166
def get_verified_chain(self):
1167-
return self._sslobj.get_verified_chain()
1167+
chain = self._sslobj.get_verified_chain()
1168+
1169+
if chain is None:
1170+
return []
1171+
1172+
return [cert.public_bytes(_ssl.ENCODING_DER) for cert in chain]
11681173

11691174
@_sslcopydoc
11701175
def get_unverified_chain(self):
1171-
return self._sslobj.get_unverified_chain()
1176+
chain = self._sslobj.get_unverified_chain()
1177+
1178+
if chain is None:
1179+
return []
1180+
1181+
return [cert.public_bytes(_ssl.ENCODING_DER) for cert in chain]
11721182

11731183
@_sslcopydoc
11741184
def selected_npn_protocol(self):

Lib/test/certdata/cert3.pem

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_ssl.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def data_file(*name):
103103

104104
# Two keys and certs signed by the same CA (for SNI tests)
105105
SIGNED_CERTFILE = data_file("keycert3.pem")
106+
SINGED_CERTFILE_ONLY = data_file("cert3.pem")
106107
SIGNED_CERTFILE_HOSTNAME = 'localhost'
107108

108109
SIGNED_CERTFILE_INFO = {
@@ -4720,6 +4721,40 @@ def test_internal_chain_client(self):
47204721
ssl.PEM_cert_to_DER_cert(pem), der
47214722
)
47224723

4724+
def test_certificate_chain(self):
4725+
client_context, server_context, hostname = testing_context(
4726+
server_chain=False
4727+
)
4728+
server = ThreadedEchoServer(context=server_context, chatty=False)
4729+
4730+
with open(SIGNING_CA) as f:
4731+
expected_ca_cert = ssl.PEM_cert_to_DER_cert(f.read())
4732+
4733+
with open(SINGED_CERTFILE_ONLY) as f:
4734+
expected_ee_cert = ssl.PEM_cert_to_DER_cert(f.read())
4735+
4736+
with server:
4737+
with client_context.wrap_socket(
4738+
socket.socket(),
4739+
server_hostname=hostname
4740+
) as s:
4741+
s.connect((HOST, server.port))
4742+
vc = s.get_verified_chain()
4743+
self.assertEqual(len(vc), 2)
4744+
4745+
ee, ca = vc
4746+
self.assertIsInstance(ee, bytes)
4747+
self.assertIsInstance(ca, bytes)
4748+
self.assertEqual(expected_ca_cert, ca)
4749+
self.assertEqual(expected_ee_cert, ee)
4750+
4751+
uvc = s.get_unverified_chain()
4752+
self.assertEqual(len(uvc), 1)
4753+
self.assertIsInstance(uvc[0], bytes)
4754+
4755+
self.assertEqual(ee, uvc[0])
4756+
self.assertNotEqual(ee, ca)
4757+
47234758
def test_internal_chain_server(self):
47244759
client_context, server_context, hostname = testing_context()
47254760
client_context.load_cert_chain(SIGNED_CERTFILE)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
You can now get the raw TLS certificate chains from TLS connections via
2+
:meth:`ssl.SSLSocket.get_verified_chain` and
3+
:meth:`ssl.SSLSocket.get_unverified_chain` methods.
4+
5+
Contributed by Mateusz Nowak.

0 commit comments

Comments
 (0)