Skip to content

Commit 858b902

Browse files
committed
Merging in fixes for subject alt name handling
2 parents 5794d65 + 168ccb5 commit 858b902

File tree

10 files changed

+68
-19
lines changed

10 files changed

+68
-19
lines changed

.pydevproject

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
22
<?eclipse-pydev version="1.0"?><pydev_project>
3-
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">ndg-httpsclient-py3</pydev_property>
4-
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
3+
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">ndg-httpsclient-py3.6</pydev_property>
4+
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.6</pydev_property>
55
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
66
<path>/ndg_httpsclient</path>
77
</pydev_pathproperty>

ndg/httpsclient/https.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class HTTPSConnection(HTTPConnection):
4848
@type default_ssl_method: int
4949
"""
5050
default_port = HTTPS_PORT
51-
default_ssl_method = SSL.SSLv23_METHOD
51+
default_ssl_method = SSL.TLSv1_2_METHOD
5252

5353
def __init__(self, host, port=None, strict=None,
5454
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, ssl_context=None):
@@ -100,6 +100,8 @@ class HTTPSContextHandler(AbstractHTTPHandler):
100100
'''
101101
https_request = AbstractHTTPHandler.do_request_
102102

103+
SSL_METHOD = SSL.TLSv1_2_METHOD
104+
103105
def __init__(self, ssl_context, debuglevel=0):
104106
"""
105107
@param ssl_context:SSL context
@@ -116,7 +118,7 @@ def __init__(self, ssl_context, debuglevel=0):
116118
ssl_context)
117119
self.ssl_context = ssl_context
118120
else:
119-
self.ssl_context = SSL.Context(SSL.TLSv1_METHOD)
121+
self.ssl_context = SSL.Context(self.__class__.SSL_METHOD)
120122

121123
def https_open(self, req):
122124
"""Opens HTTPS request

ndg/httpsclient/ssl_context_util.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def make_ssl_context_from_config(ssl_config=False, url=None):
4141

4242

4343
def make_ssl_context(key_file=None, cert_file=None, pem_file=None, ca_dir=None,
44-
verify_peer=False, url=None, method=SSL.TLSv1_METHOD,
44+
verify_peer=False, url=None, method=SSL.TLSv1_2_METHOD,
4545
key_file_passphrase=None):
4646
"""
4747
Creates SSL context containing certificate and key file locations.
@@ -64,6 +64,8 @@ def make_ssl_context(key_file=None, cert_file=None, pem_file=None, ca_dir=None,
6464

6565
if pem_file or ca_dir:
6666
ssl_context.load_verify_locations(pem_file, ca_dir)
67+
else:
68+
ssl_context.set_default_verify_paths() # Use OS CA bundle
6769

6870
def _callback(conn, x509, errnum, errdepth, preverify_ok):
6971
"""Default certification verification callback.

ndg/httpsclient/ssl_peer_verification.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ServerSSLCertVerification(object):
4040
'domainComponent': 'DC',
4141
'userid': 'UID'
4242
}
43-
SUBJ_ALT_NAME_EXT_NAME = 'subjectAltName'
43+
SUBJ_ALT_NAME_EXT_NAME = b'subjectAltName'
4444
PARSER_RE_STR = '/(%s)=' % '|'.join(list(DN_LUT.keys()) + \
4545
list(DN_LUT.values()))
4646
PARSER_RE = re.compile(PARSER_RE_STR)

ndg/httpsclient/subj_alt_name.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020
'is: %s' % e)
2121
import warnings
2222
warnings.warn(import_error_msg)
23+
barp
2324
class Pyasn1ImportError(ImportError):
2425
"Raise for pyasn1 import error"
2526
raise Pyasn1ImportError(import_error_msg)
2627

2728

28-
MAX = 64
29+
MAX = 1024
2930

3031

3132
class DirectoryString(univ.Choice):

ndg/httpsclient/test/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ class Constants(object):
1616
'''Convenience base class from which other unit tests can extend. Its
1717
sets the generic data directory path'''
1818
PORT = 4443
19+
# PORT = 443
1920
PORT2 = 4444
2021
HOSTNAME = 'localhost'
22+
# HOSTNAME = 'files.pythonhosted.org'
2123
TEST_URI = 'https://%s:%d' % (HOSTNAME, PORT)
2224
TEST_URI2 = 'https://%s:%d' % (HOSTNAME, PORT2)
23-
25+
2426
UNITTEST_DIR = os.path.dirname(os.path.abspath(__file__))
2527
CACERT_DIR = os.path.join(UNITTEST_DIR, 'pki', 'ca')
2628
SSL_CERT_FILENAME = 'localhost.crt'

ndg/httpsclient/test/test_https.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test02_open_fails(self):
3838
self.assertRaises(socket.error, conn.connect)
3939

4040
def test03_ssl_verification_of_peer_fails(self):
41-
ctx = SSL.Context(SSL.TLSv1_METHOD)
41+
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
4242

4343
def verify_callback(conn, x509, errnum, errdepth, preverify_ok):
4444
log.debug('SSL peer certificate verification failed for %r',
@@ -57,7 +57,7 @@ def verify_callback(conn, x509, errnum, errdepth, preverify_ok):
5757
self.assertRaises(SSL.Error, conn.request, 'GET', '/')
5858

5959
def test03_ssl_verification_of_peer_succeeds(self):
60-
ctx = SSL.Context(SSL.TLSv1_METHOD)
60+
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
6161

6262
verify_callback = lambda conn, x509, errnum, errdepth, preverify_ok: \
6363
preverify_ok
@@ -76,7 +76,7 @@ def test03_ssl_verification_of_peer_succeeds(self):
7676
print('Response = %s' % resp.read())
7777

7878
def test04_ssl_verification_with_subj_alt_name(self):
79-
ctx = SSL.Context(SSL.TLSv1_METHOD)
79+
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
8080

8181
verification = ServerSSLCertVerification(hostname='localhost')
8282
verify_callback = verification.get_verify_server_cert_func()
@@ -95,7 +95,7 @@ def test04_ssl_verification_with_subj_alt_name(self):
9595
print('Response = %s' % resp.read())
9696

9797
def test04_ssl_verification_with_subj_common_name(self):
98-
ctx = SSL.Context(SSL.TLSv1_METHOD)
98+
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
9999

100100
# Explicitly set verification of peer hostname using peer certificate
101101
# subject common name

ndg/httpsclient/test/test_urllib2.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@
1212
import sys
1313

1414
if sys.version_info[0] > 2:
15-
from urllib.error import URLError as URLError_
15+
from urllib.error import URLError as URLError
1616
else:
17-
from urllib2 import URLError as URLError_
17+
from urllib2 import URLError as URLError
1818

1919
import unittest
2020

2121
from OpenSSL import SSL
2222
from ndg.httpsclient.test import Constants
2323
from ndg.httpsclient.urllib2_build_opener import build_opener
24+
from ndg.httpsclient.ssl_peer_verification import ServerSSLCertVerification
2425

2526

2627
class Urllib2TestCase(unittest.TestCase):
@@ -36,21 +37,53 @@ def test02_open(self):
3637
self.assertTrue(res)
3738
print("res = %s" % res.read())
3839

40+
# Skip this test for remote service as it can take a long time to timeout
41+
@unittest.skipIf(Constants.HOSTNAME != 'localhost', 'Skip non-local host')
3942
def test03_open_fails_unknown_loc(self):
4043
opener = build_opener()
41-
self.assertRaises(URLError_, opener.open, Constants.TEST_URI2)
44+
self.assertRaises(URLError, opener.open, Constants.TEST_URI2)
4245

4346
def test04_open_peer_cert_verification_fails(self):
4447
# Explicitly set empty CA directory to make verification fail
45-
ctx = SSL.Context(SSL.TLSv1_METHOD)
48+
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
4649
verify_callback = lambda conn, x509, errnum, errdepth, preverify_ok: \
4750
preverify_ok
4851

4952
ctx.set_verify(SSL.VERIFY_PEER, verify_callback)
5053
ctx.load_verify_locations(None, './')
5154
opener = build_opener(ssl_context=ctx)
5255
self.assertRaises(SSL.Error, opener.open, Constants.TEST_URI)
56+
57+
def test05_open_with_subj_alt_names_verification(self):
58+
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
59+
60+
# Set wildcard hostname for subject alternative name matching -
61+
# setting a minimum of two name components for hostname
62+
split_hostname = Constants.HOSTNAME.split('.', 1)
63+
if len(split_hostname) > 1:
64+
_hostname = '*.' + split_hostname[-1]
65+
else:
66+
_hostname = Constants.HOSTNAME
67+
68+
server_ssl_verify = ServerSSLCertVerification(hostname=_hostname)
69+
verify_callback_ = server_ssl_verify.get_verify_server_cert_func()
70+
ctx.set_verify(SSL.VERIFY_PEER, verify_callback_)
71+
72+
# Set default verify paths if testing with peer that has corresponding
73+
# CA cert in bundle provided with the OS. In this case, load verify
74+
# locations is not needed.
75+
#ctx.set_default_verify_paths()
5376

77+
ctx.set_verify_depth(9)
78+
79+
# Set correct location for CA certs to verify with
80+
ctx.load_verify_locations(None, Constants.CACERT_DIR)
81+
82+
opener = build_opener(ssl_context=ctx)
83+
res = opener.open(Constants.TEST_URI)
84+
self.assertTrue(res)
85+
print("res = %s" % res.read())
86+
5487

5588
if __name__ == "__main__":
5689
unittest.main()

ndg/httpsclient/test/test_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ class TestUtilsModule(unittest.TestCase):
2323
'''Test ndg.httpsclient.utils module'''
2424

2525
def test01_configuration(self):
26-
config = Configuration(SSL.Context(SSL.TLSv1_METHOD), True)
26+
config = Configuration(SSL.Context(SSL.TLSv1_2_METHOD), True)
2727
self.assertTrue(config.ssl_context)
2828
self.assertEqual(config.debug, True)
2929

3030
def test02_fetch_from_url(self):
31-
config = Configuration(SSL.Context(SSL.TLSv1_METHOD), True)
31+
config = Configuration(SSL.Context(SSL.TLSv1_2_METHOD), True)
3232
res = fetch_from_url(Constants.TEST_URI, config)
3333
self.assertTrue(res)
3434

3535
def test03_open_url(self):
36-
config = Configuration(SSL.Context(SSL.TLSv1_METHOD), True)
36+
config = Configuration(SSL.Context(SSL.TLSv1_2_METHOD), True)
3737
res = open_url(Constants.TEST_URI, config)
3838
self.assertEqual(res[0], 200,
3939
'open_url for %r failed' % Constants.TEST_URI)

setup.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
1919
Releases
2020
========
21+
0.5.0
22+
-----
23+
* Fix to Subject Alternative Name verification to handle certificates with up
24+
to 1024 names (previously it was 64). Tested with
25+
https://files.pythonhosted.org. Thanks to Matt Pegler for flagging up
26+
* Fix defaults in SSL context objects and in tests to use TLS 1.2
27+
* Use byte type for ``subjectAltName`` field matching
28+
* Added additional Subject Alternative Name test - in ``test_urlib2``
29+
2130
0.4.4
2231
-----
2332
* Update test certificate files.

0 commit comments

Comments
 (0)