Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit e32b5be

Browse files
committed
Support for client side certificate in tls.init_context
1 parent 6ec4566 commit e32b5be

File tree

3 files changed

+46
-12
lines changed

3 files changed

+46
-12
lines changed

hyper/contrib.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from hyper.common.connection import HTTPConnection
1818
from hyper.compat import urlparse
19+
from hyper.tls import init_context
1920

2021

2122
class HTTP20Adapter(HTTPAdapter):
@@ -39,17 +40,8 @@ def get_connection(self, host, port, scheme, cert=None):
3940
port = 80 if not secure else 443
4041

4142
ssl_context = None
42-
4343
if cert is not None:
44-
ssl_context = init_context()
45-
try:
46-
basestring
47-
except NameError:
48-
basestring = str
49-
if not isinstance(cert, basestring):
50-
ssl_context.load_cert_chain(cert[0], cert[1])
51-
else:
52-
ssl_context.load_cert_chain(cert)
44+
ssl_context = init_context(cert=cert)
5345

5446
try:
5547
conn = self.connections[(host, port, scheme, cert)]

hyper/tls.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def wrap_socket(sock, server_hostname, ssl_context=None):
6363
return (ssl_sock, proto)
6464

6565

66-
def init_context(cert_path=None):
66+
def init_context(cert_path=None, cert=None, cert_password=None):
6767
"""
6868
Create a new ``SSLContext`` that is correctly set up for an HTTP/2 connection.
6969
This SSL context object can be customized and passed as a parameter to the
@@ -72,7 +72,24 @@ def init_context(cert_path=None):
7272
certificate. The path to the certificate can be absolute or relative
7373
to your working directory.
7474
75-
:param cert_path: (optional) The path to the certificate file.
75+
:param cert_path: (optional) The path to the certificate file of
76+
“certification authority” (CA) certificates
77+
:param cert: (optional) if string, path to ssl client cert file (.pem).
78+
If tuple, ('cert', 'key') pair.
79+
The certfile string must be the path to a single file in PEM format
80+
containing the certificate as well as any number of CA certificates
81+
needed to establish the certificate’s authenticity. The keyfile string,
82+
if present, must point to a file containing the private key in.
83+
Otherwise the private key will be taken from certfile as well.
84+
:param cert_password: (optional) The password argument may be a function to
85+
call to get the password for decrypting the private key. It will only
86+
be called if the private key is encrypted and a password is necessary.
87+
It will be called with no arguments, and it should return a string,
88+
bytes, or bytearray. If the return value is a string it will be
89+
encoded as UTF-8 before using it to decrypt the key. Alternatively a
90+
string, bytes, or bytearray value may be supplied directly as the
91+
password argument. It will be ignored if the private key is not
92+
encrypted and no password is needed.
7693
:returns: An ``SSLContext`` correctly set up for HTTP/2.
7794
"""
7895
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
@@ -90,4 +107,14 @@ def init_context(cert_path=None):
90107
# required by the spec
91108
context.options |= ssl.OP_NO_COMPRESSION
92109

110+
if cert is not None:
111+
try:
112+
basestring
113+
except NameError:
114+
basestring = str
115+
if not isinstance(cert, basestring):
116+
context.load_cert_chain(cert[0], cert[1], cert_password)
117+
else:
118+
context.load_cert_chain(cert, password=cert_password)
119+
93120
return context

test/test_SSLContext.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
22
"""
33
Tests the hyper SSLContext.
44
"""
5+
import os
6+
57
import hyper
68
from hyper.common.connection import HTTPConnection
79
from hyper.compat import ssl
10+
811
import pytest
912

13+
14+
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
15+
TEST_CERTS_DIR = os.path.join(TEST_DIR, 'certs')
16+
CLIENT_CERT_FILE = os.path.join(TEST_CERTS_DIR, 'client.crt')
17+
CLIENT_KEY_FILE = os.path.join(TEST_CERTS_DIR, 'client.key')
18+
19+
1020
class TestSSLContext(object):
1121
"""
1222
Tests default and custom SSLContext
@@ -47,3 +57,8 @@ def test_HTTPConnection_with_custom_context(self):
4757
assert conn.ssl_context.check_hostname == True
4858
assert conn.ssl_context.verify_mode == ssl.CERT_REQUIRED
4959
assert conn.ssl_context.options & ssl.OP_NO_COMPRESSION != 0
60+
61+
62+
def test_client_certificates(self):
63+
context = hyper.tls.init_context(
64+
cert=(CLIENT_CERT_FILE, CLIENT_KEY_FILE), cert_password='abc123')

0 commit comments

Comments
 (0)