From 3a6d7cd9bc9ff7a82e9dc560c9118ddcc4607dec Mon Sep 17 00:00:00 2001 From: Sergey Shepelev Date: Thu, 26 Sep 2019 22:11:25 +0300 Subject: tests: TLS/https support --- script/generate-tls | 61 ++++++++++++++ tests/__init__.py | 79 ++++++++++++++++-- tests/test_external.py | 129 ----------------------------- tests/test_http.py | 49 ++--------- tests/test_https.py | 179 +++++++++++++++++++++++++++++++++++++++++ tests/tls/ca.key | 27 +++++++ tests/tls/ca.pem | 20 +++++ tests/tls/ca.srl | 1 + tests/tls/ca_unused.pem | 20 +++++ tests/tls/client.crt | 20 +++++ tests/tls/client.key | 27 +++++++ tests/tls/client.pem | 47 +++++++++++ tests/tls/client_chain.pem | 67 +++++++++++++++ tests/tls/client_encrypted.crt | 20 +++++ tests/tls/client_encrypted.key | 30 +++++++ tests/tls/client_encrypted.pem | 50 ++++++++++++ tests/tls/server.crt | 20 +++++ tests/tls/server.key | 27 +++++++ tests/tls/server.pem | 47 +++++++++++ tests/tls/server_chain.pem | 67 +++++++++++++++ 20 files changed, 808 insertions(+), 179 deletions(-) create mode 100755 script/generate-tls delete mode 100644 tests/test_external.py create mode 100644 tests/test_https.py create mode 100644 tests/tls/ca.key create mode 100644 tests/tls/ca.pem create mode 100644 tests/tls/ca.srl create mode 100644 tests/tls/ca_unused.pem create mode 100644 tests/tls/client.crt create mode 100644 tests/tls/client.key create mode 100644 tests/tls/client.pem create mode 100644 tests/tls/client_chain.pem create mode 100644 tests/tls/client_encrypted.crt create mode 100644 tests/tls/client_encrypted.key create mode 100644 tests/tls/client_encrypted.pem create mode 100644 tests/tls/server.crt create mode 100644 tests/tls/server.key create mode 100644 tests/tls/server.pem create mode 100644 tests/tls/server_chain.pem diff --git a/script/generate-tls b/script/generate-tls new file mode 100755 index 0000000..8c96f1e --- /dev/null +++ b/script/generate-tls @@ -0,0 +1,61 @@ +#!/bin/bash +set -eu + +target_dir="${1:-.}" +days=3650 +rsa_bits=2048 +org="httplib2-test" +server_cn="localhost" +subj_prefix="/C=ZZ/ST=./L=./O=$org/OU=." + +main() { + cd "$target_dir" + gen + check +} + +check() { + echo "- check keys" >&2 + openssl rsa -in ca.key -check -noout + openssl rsa -in client.key -check -noout + openssl rsa -in client_encrypted.key -check -noout -passin pass:12345 + openssl rsa -in server.key -check -noout + + echo "- check certs" >&2 + for f in *.pem ; do + openssl x509 -in "$f" -checkend 3600 -noout + done +} + +gen() { + echo "- generate keys, if absent" >&2 + [[ -f ca.key ]] || openssl genrsa -out ca.key $rsa_bits + [[ -f client.key ]] || openssl genrsa -out client.key $rsa_bits + [[ -f client_encrypted.key ]] || openssl rsa -in client.key -out client_encrypted.key -aes128 -passout pass:12345 + [[ -f server.key ]] || openssl genrsa -out server.key $rsa_bits + + echo "- generate CA" >&2 + openssl req -batch -new -nodes -x509 -days $days -subj "$subj_prefix/CN=$org-CA" -key ca.key -out ca.pem + openssl req -batch -new -nodes -x509 -days $days -subj "$subj_prefix/CN=$org-CA-unused" -key ca.key -out ca_unused.pem + + echo "- generate client cert" >&2 + openssl req -batch -new -nodes -out tmp.csr -key client.key -subj "$subj_prefix/CN=$org-client" + openssl x509 -req -in tmp.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.crt -days $days -serial -fingerprint + cat client.crt client.key >client.pem + cat client.crt ca.pem client.key >client_chain.pem + + echo "- generate encrypted client cert" >&2 + openssl req -batch -new -nodes -out tmp.csr -key client_encrypted.key -passin pass:12345 -subj "$subj_prefix/CN=$org-client-enc" + openssl x509 -req -in tmp.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client_encrypted.crt -days $days -serial -fingerprint + cat client_encrypted.crt client_encrypted.key >client_encrypted.pem + + echo "- generate server cert" >&2 + openssl req -batch -new -nodes -out tmp.csr -key server.key -subj "$subj_prefix/CN=$server_cn" + openssl x509 -req -in tmp.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.crt -days $days -serial -fingerprint + cat server.crt server.key >server.pem + cat server.crt ca.pem server.key >server_chain.pem + + rm tmp.csr +} + +main diff --git a/tests/__init__.py b/tests/__init__.py index 28959d3..496652b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -14,6 +14,7 @@ import re import shutil import six import socket +import ssl import struct import sys import threading @@ -23,6 +24,18 @@ import zlib from six.moves import http_client, queue +DUMMY_URL = "http://127.0.0.1:1" +DUMMY_HTTPS_URL = "https://127.0.0.1:2" + +tls_dir = os.path.join(os.path.dirname(__file__), "tls") +CA_CERTS = os.path.join(tls_dir, "ca.pem") +CA_UNUSED_CERTS = os.path.join(tls_dir, "ca_unused.pem") +CLIENT_PEM = os.path.join(tls_dir, "client.pem") +CLIENT_ENCRYPTED_PEM = os.path.join(tls_dir, "client_encrypted.pem") +SERVER_PEM = os.path.join(tls_dir, "server.pem") +SERVER_CHAIN = os.path.join(tls_dir, "server_chain.pem") + + @contextlib.contextmanager def assert_raises(exc_type): def _name(t): @@ -261,9 +274,29 @@ class MockHTTPBadStatusConnection(object): @contextlib.contextmanager -def server_socket(fun, request_count=1, timeout=5): +def server_socket(fun, request_count=1, timeout=5, scheme="", tls=None): + """Base socket server for tests. + Likely you want to use server_request or other higher level helpers. + All arguments except fun can be passed to other server_* helpers. + + :param fun: fun(client_sock, tick) called after successful accept(). + :param request_count: test succeeds after exactly this number of requests, triggered by tick(request) + :param timeout: seconds. + :param scheme: affects yielded value + "" - build normal http/https URI. + string - build normal URI using supplied scheme. + None - yield (addr, port) tuple. + :param tls: + None (default) - plain HTTP. + True - HTTPS with reasonable defaults. Likely you want httplib2.Http(ca_certs=tests.CA_CERTS) + string - path to custom server cert+key PEM file. + callable - function(context, listener, skip_errors) -> ssl_wrapped_listener + """ gresult = [None] gcounter = [0] + tls_skip_errors = [ + "TLSV1_ALERT_UNKNOWN_CA", + ] def tick(request): gcounter[0] += 1 @@ -276,7 +309,13 @@ def server_socket(fun, request_count=1, timeout=5): def server_socket_thread(srv): try: while gcounter[0] < request_count: - client, _ = srv.accept() + try: + client, _ = srv.accept() + except ssl.SSLError as e: + if e.reason in tls_skip_errors: + return + raise + try: client.settimeout(timeout) fun(client, tick) @@ -299,18 +338,36 @@ def server_socket(fun, request_count=1, timeout=5): print(traceback.format_exc(), file=sys.stderr) gresult[0] = e + bind_hostname = "localhost" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server.bind(("localhost", 0)) + server.bind((bind_hostname, 0)) try: server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) except socket.error as ex: print("non critical error on SO_REUSEADDR", ex) server.listen(10) server.settimeout(timeout) + server_port = server.getsockname()[1] + if tls is True: + tls = SERVER_CHAIN + if tls: + context = ssl_context() + if callable(tls): + context.load_cert_chain(SERVER_CHAIN) + server = tls(context, server, tls_skip_errors) + else: + context.load_cert_chain(tls) + server = context.wrap_socket(server, server_side=True) + if scheme == "": + scheme = "https" if tls else "http" + t = threading.Thread(target=server_socket_thread, args=(server,)) t.daemon = True t.start() - yield u"http://{0}:{1}/".format(*server.getsockname()) + if scheme is None: + yield (bind_hostname, server_port) + else: + yield u"{scheme}://{host}:{port}/".format(scheme=scheme, host=bind_hostname, port=server_port) server.close() t.join() if gresult[0] is not None: @@ -329,11 +386,12 @@ def server_yield(fun, **kwargs): if request is None: break i += 1 - request.client_addr = sock.getsockname() + request.client_sock = sock request.number = i q.put(request) response = six.next(g) sock.sendall(response) + request.client_sock = None if not tick(request): break @@ -349,10 +407,11 @@ def server_request(request_handler, **kwargs): if request is None: break i += 1 - request.client_addr = sock.getsockname() + request.client_sock = sock request.number = i response = request_handler(request=request) sock.sendall(response) + request.client_sock = None if not tick(request): break @@ -685,3 +744,11 @@ def deflate_compress(bs): def deflate_decompress(bs): return zlib.decompress(bs, -zlib.MAX_WBITS) + + +def ssl_context(protocol=None): + """Workaround for old SSLContext() required protocol argument. + """ + if sys.version_info < (3, 5, 3): + return ssl.SSLContext(ssl.PROTOCOL_SSLv23) + return ssl.SSLContext() diff --git a/tests/test_external.py b/tests/test_external.py deleted file mode 100644 index 0628d96..0000000 --- a/tests/test_external.py +++ /dev/null @@ -1,129 +0,0 @@ -"""These tests rely on replies from public internet services - -TODO: reimplement with local stubs -""" -import httplib2 -import os -import pytest -import ssl -import sys -import tests - - -def test_get_301_via_https(): - # Google always redirects to http://google.com - http = httplib2.Http() - response, content = http.request("https://code.google.com/apis/", "GET") - assert response.status == 200 - assert response.previous.status == 301 - - -def test_get_via_https(): - # Test that we can handle HTTPS - http = httplib2.Http() - response, content = http.request("https://google.com/adsense/", "GET") - assert response.status == 200 - - -def test_get_via_https_spec_violation_on_location(): - # Test that we follow redirects through HTTPS - # even if they violate the spec by including - # a relative Location: header instead of an - # absolute one. - http = httplib2.Http() - response, content = http.request("https://google.com/adsense", "GET") - assert response.status == 200 - assert response.previous is not None - - -def test_get_via_https_key_cert(): - # At this point I can only test - # that the key and cert files are passed in - # correctly to httplib. It would be nice to have - # a real https endpoint to test against. - http = httplib2.Http(timeout=2) - http.add_certificate("akeyfile", "acertfile", "bitworking.org") - try: - http.request("https://bitworking.org", "GET") - except AttributeError: - assert http.connections["https:bitworking.org"].key_file == "akeyfile" - assert http.connections["https:bitworking.org"].cert_file == "acertfile" - except IOError: - # Skip on 3.2 - pass - - try: - http.request("https://notthere.bitworking.org", "GET") - except httplib2.ServerNotFoundError: - assert http.connections["https:notthere.bitworking.org"].key_file is None - assert http.connections["https:notthere.bitworking.org"].cert_file is None - except IOError: - # Skip on 3.2 - pass - - -def test_ssl_invalid_ca_certs_path(): - # Test that we get an ssl.SSLError when specifying a non-existent CA - # certs file. - http = httplib2.Http(ca_certs="/nosuchfile") - with tests.assert_raises(IOError): - http.request("https://www.google.com/", "GET") - - -@pytest.mark.xfail( - sys.version_info <= (3,), - reason=( - "FIXME: for unknown reason Python 2.7.10 validates www.google.com " - "against dummy CA www.example.com" - ), -) -def test_ssl_wrong_ca(): - # Test that we get a SSLHandshakeError if we try to access - # https://www.google.com, using a CA cert file that doesn't contain - # the CA Google uses (i.e., simulating a cert that's not signed by a - # trusted CA). - other_ca_certs = os.path.join( - os.path.dirname(os.path.abspath(httplib2.__file__)), "test", "other_cacerts.txt" - ) - assert os.path.exists(other_ca_certs) - http = httplib2.Http(ca_certs=other_ca_certs) - http.follow_redirects = False - with tests.assert_raises(ssl.SSLError): - http.request("https://www.google.com/", "GET") - - -def test_sni_hostname_validation(): - # TODO: make explicit test server with SNI validation - http = httplib2.Http() - http.request("https://google.com/", method="GET") - - -@pytest.mark.skipif( - os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"), - reason="Python 2.7 doesn't support TLS min/max" -) -def test_min_tls_version(): - # skip on Python versions that don't support TLS min - if not hasattr(ssl.SSLContext(), 'minimum_version'): - return - # BadSSL server that supports max TLS 1.1, - # forcing 1.2 should always fail - http = httplib2.Http(tls_minimum_version="TLSv1_2") - with tests.assert_raises(ssl.SSLError): - http.request("https://tls-v1-1.badssl.com:1011/") - - -@pytest.mark.skipif( - os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"), - reason="Python 2.7 doesn't support TLS min/max" -) -def test_max_tls_version(): - # skip on Python versions that don't support TLS max - if not hasattr(ssl.SSLContext(), 'maximum_version'): - return - # Google supports TLS 1.2+, confirm we can force down to 1.0 - # this may break whenever Google disables TLSv1 - http = httplib2.Http(tls_maximum_version="TLSv1") - http.request("https://google.com") - _, tls_ver, _ = http.connections['https:google.com'].sock.cipher() - assert tls_ver == "TLSv1.0" diff --git a/tests/test_http.py b/tests/test_http.py index 9bd9ee0..97b52dc 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -10,12 +10,8 @@ import os import pytest from six.moves import http_client, urllib import socket -import ssl import tests -DUMMY_URL = "http://127.0.0.1:1" -DUMMY_HTTPS_URL = "https://127.0.0.1:2" - def _raise_connection_refused_exception(*args, **kwargs): raise socket.error(errno.ECONNREFUSED, "Connection refused.") @@ -25,9 +21,9 @@ def test_connection_type(): http = httplib2.Http() http.force_exception_to_status_code = False response, content = http.request( - DUMMY_URL, connection_type=tests.MockHTTPConnection + tests.DUMMY_URL, connection_type=tests.MockHTTPConnection ) - assert response["content-location"] == DUMMY_URL + assert response["content-location"] == tests.DUMMY_URL assert content == b"the body" @@ -38,7 +34,7 @@ def test_bad_status_line_retry(): http.force_exception_to_status_code = False try: response, content = http.request( - DUMMY_URL, connection_type=tests.MockHTTPBadStatusConnection + tests.DUMMY_URL, connection_type=tests.MockHTTPBadStatusConnection ) except http_client.BadStatusLine: assert tests.MockHTTPBadStatusConnection.num_calls == 2 @@ -71,7 +67,7 @@ def test_connection_refused_raises_exception(mock_socket_connect): http = httplib2.Http() http.force_exception_to_status_code = False with tests.assert_raises(socket.error): - http.request(DUMMY_URL) + http.request(tests.DUMMY_URL) @pytest.mark.skipif( @@ -84,7 +80,7 @@ def test_connection_refused_returns_response(mock_socket_connect): mock_socket_connect.side_effect = _raise_connection_refused_exception http = httplib2.Http() http.force_exception_to_status_code = True - response, content = http.request(DUMMY_URL) + response, content = http.request(tests.DUMMY_URL) content = content.lower() assert response["content-type"] == "text/plain" assert ( @@ -647,38 +643,3 @@ content""" assert response.status == 200 assert content == b"content" assert response["link"], "link1, link2" - - -@pytest.mark.skipif( - os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"), - reason="Python 2.7 doesn't support TLS min/max" -) -def test_set_min_tls_version(): - # Test setting minimum TLS version - # We expect failure on Python < 3.7 or OpenSSL < 1.1 - expect_success = hasattr(ssl.SSLContext(), 'minimum_version') - try: - http = httplib2.Http(tls_minimum_version="TLSv1_2") - http.request(DUMMY_HTTPS_URL) - except RuntimeError: - assert not expect_success - except socket.error: - assert expect_success - - -@pytest.mark.skipif( - os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"), - reason="Python 2.7 doesn't support TLS min/max" -) -def test_set_max_tls_version(): - # Test setting maximum TLS version - # We expect RuntimeError on Python < 3.7 or OpenSSL < 1.1 - # We expect socket error otherwise - expect_success = hasattr(ssl.SSLContext(), 'maximum_version') - try: - http = httplib2.Http(tls_maximum_version="TLSv1_2") - http.request(DUMMY_HTTPS_URL) - except RuntimeError: - assert not expect_success - except socket.error: - assert expect_success diff --git a/tests/test_https.py b/tests/test_https.py new file mode 100644 index 0000000..f494d7a --- /dev/null +++ b/tests/test_https.py @@ -0,0 +1,179 @@ +import httplib2 +import pytest +from six.moves import urllib +import socket +import ssl +import tests + + +def test_get_via_https(): + # Test that we can handle HTTPS + http = httplib2.Http(ca_certs=tests.CA_CERTS) + with tests.server_const_http(tls=True) as uri: + response, _ = http.request(uri, "GET") + assert response.status == 200 + + +def test_get_301_via_https(): + http = httplib2.Http(ca_certs=tests.CA_CERTS) + glocation = [""] # nonlocal kind of trick, maybe redundant + + def handler(request): + if request.uri == "/final": + return tests.http_response_bytes(body=b"final") + return tests.http_response_bytes(status="301 goto", headers={"location": glocation[0]}) + + with tests.server_request(handler, request_count=2, tls=True) as uri: + glocation[0] = urllib.parse.urljoin(uri, "/final") + response, content = http.request(uri, "GET") + assert response.status == 200 + assert content == b"final" + assert response.previous.status == 301 + assert response.previous["location"] == glocation[0] + + +def test_get_301_via_https_spec_violation_on_location(): + # Test that we follow redirects through HTTPS + # even if they violate the spec by including + # a relative Location: header instead of an absolute one. + http = httplib2.Http(ca_certs=tests.CA_CERTS) + + def handler(request): + if request.uri == "/final": + return tests.http_response_bytes(body=b"final") + return tests.http_response_bytes(status="301 goto", headers={"location": "/final"}) + + with tests.server_request(handler, request_count=2, tls=True) as uri: + response, content = http.request(uri, "GET") + assert response.status == 200 + assert content == b"final" + assert response.previous.status == 301 + + +def test_invalid_ca_certs_path(): + http = httplib2.Http(ca_certs="/nosuchfile") + with tests.server_const_http(request_count=0, tls=True) as uri: + with tests.assert_raises(IOError): + http.request(uri, "GET") + + +def test_not_trusted_ca(): + # Test that we get a SSLHandshakeError if we try to access + # server using a CA cert file that doesn't contain server's CA. + http = httplib2.Http(ca_certs=tests.CA_UNUSED_CERTS) + with tests.server_const_http(tls=True) as uri: + try: + http.request(uri, "GET") + assert False, "expected CERTIFICATE_VERIFY_FAILED" + except ssl.SSLError as e: + assert e.reason == "CERTIFICATE_VERIFY_FAILED" + except httplib2.SSLHandshakeError: # Python2 + pass + + +@pytest.mark.skipif( + not hasattr(tests.ssl_context(), "minimum_version"), + reason="ssl doesn't support TLS min/max", +) +def test_set_min_tls_version(): + # Test setting minimum TLS version + # We expect failure on Python < 3.7 or OpenSSL < 1.1 + expect_success = hasattr(ssl.SSLContext(), 'minimum_version') + try: + http = httplib2.Http(tls_minimum_version="TLSv1_2") + http.request(tests.DUMMY_HTTPS_URL) + except RuntimeError: + assert not expect_success + except socket.error: + assert expect_success + + +@pytest.mark.skipif( + not hasattr(tests.ssl_context(), "maximum_version"), + reason="ssl doesn't support TLS min/max", +) +def test_set_max_tls_version(): + # Test setting maximum TLS version + # We expect RuntimeError on Python < 3.7 or OpenSSL < 1.1 + # We expect socket error otherwise + expect_success = hasattr(ssl.SSLContext(), 'maximum_version') + try: + http = httplib2.Http(tls_maximum_version="TLSv1_2") + http.request(tests.DUMMY_HTTPS_URL) + except RuntimeError: + assert not expect_success + except socket.error: + assert expect_success + + +@pytest.mark.skipif( + not hasattr(tests.ssl_context(), "minimum_version"), + reason="ssl doesn't support TLS min/max", +) +def test_min_tls_version(): + def setup_tls(context, server, skip_errors): + skip_errors.append("WRONG_VERSION_NUMBER") + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1) + context.load_cert_chain(tests.SERVER_CHAIN) + return context.wrap_socket(server, server_side=True) + + http = httplib2.Http(ca_certs=tests.CA_CERTS, tls_minimum_version="TLSv1_2") + with tests.server_const_http(tls=setup_tls) as uri: + try: + http.request(uri) + assert False, "expected SSLError" + except ssl.SSLError as e: + assert e.reason in ("UNSUPPORTED_PROTOCOL", "VERSION_TOO_LOW") + + +@pytest.mark.skipif( + not hasattr(tests.ssl_context(), "maximum_version"), + reason="ssl doesn't support TLS min/max", +) +def test_max_tls_version(): + http = httplib2.Http(ca_certs=tests.CA_CERTS, tls_maximum_version="TLSv1") + with tests.server_const_http(tls=True) as uri: + http.request(uri) + _, tls_ver, _ = http.connections.popitem()[1].sock.cipher() + assert tls_ver == "TLSv1.0" + + +def test_client_cert_verified(): + cert_log = [] + + def setup_tls(context, server, skip_errors): + context.load_verify_locations(cafile=tests.CA_CERTS) + context.verify_mode = ssl.CERT_REQUIRED + return context.wrap_socket(server, server_side=True) + + def handler(request): + cert_log.append(request.client_sock.getpeercert()) + return tests.http_response_bytes() + + http = httplib2.Http(ca_certs=tests.CA_CERTS) + with tests.server_request(handler, tls=setup_tls) as uri: + uri_parsed = urllib.parse.urlparse(uri) + http.add_certificate(tests.CLIENT_PEM, tests.CLIENT_PEM, uri_parsed.netloc) + http.request(uri) + + assert len(cert_log) == 1 + # TODO extract serial from tests.CLIENT_PEM + assert cert_log[0]["serialNumber"] == "E2AA6A96D1BF1AEC" + + +@pytest.mark.skipif( + not hasattr(tests.ssl_context(), "set_servername_callback"), + reason="SSLContext.set_servername_callback is not available", +) +def test_sni_set_servername_callback(): + sni_log = [] + + def setup_tls(context, server, skip_errors): + context.set_servername_callback(lambda _sock, hostname, _context: sni_log.append(hostname)) + return context.wrap_socket(server, server_side=True) + + http = httplib2.Http(ca_certs=tests.CA_CERTS) + with tests.server_const_http(tls=setup_tls) as uri: + uri_parsed = urllib.parse.urlparse(uri) + http.request(uri) + assert sni_log == [uri_parsed.hostname] diff --git a/tests/tls/ca.key b/tests/tls/ca.key new file mode 100644 index 0000000..cc1bb1a --- /dev/null +++ b/tests/tls/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrjmlTH +PFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gHsQn3 +3Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOENh5r +lYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0QByP +PACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwCPspC ++wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABAoIBAE+7KqAPmkIeC1Rg +2/PjtHwUFhwfk/MblIPGm/+38a0c1bT6aJJWbYUS9jhvIZDNQeT8GkrUVKhhnfE0 +Fl4oxPQXGNpJbR0657o11xiZo8QZt5b8cLhGTsY7gFd2jrKBDEgMZ0JsdqCO/m0Q +pp1KEcelnQBKhHj0UVHnYtVaVo/T0ciS06l0urxpXBW2Bcg5lP/KzBD9md9UJrPU +9oPM2snMlBTGKH79eTZu4eyKWOMIK+0vSLBnJkAZtNjvitftEs+a0AvVSF+uXbow +sz7WUpm+BR2wTKikfNMa5nNaLUCdE3sB/1fHQeT7reLEfIVrIfHCyNHi2X0xwWBP +n9U/Q1ECgYEA/qT9JhIZsy1HjHNliFhfW1lBMLRZdiQwyQeoV0N1aMy68rV3kDqr +z8qIaHRt2zCgbKgEhB2WRuIL57mXK3WRhk3KP5LEG0wKr7n58iiTtipH/I/04rTG +RAcYUIR8gDpvd0P1YUJ4dSMqPLDP13+ikA8C3EfL78nxMeCdhyuBA0cCgYEAxR3Q +Smjkcs8pckl04qOZnnGRpKg/Hmu2wIg2WNisI97t35B0dkZhTOGfsCuMOWJixzEp +35ZgUzWUd3ACrgZQcxUYlBAdo849QE7lx1Nys8kouPEjbVsRH8Fs1JJkEiVYhoWV +8JfVzv6Mb95ZBGlvYsiq6p9hT0mLykDNt0xG8o0CgYBOHj1O1ZSuxABEFQ6b0kiG +lI4MK/eZ56ZTtZauFpLJMK1VUdg5Fdapaz+Hk9gzuuosCys/gHgejLAMSYIXofyf +z/Nwp0yj9yL8H7iO0mXmJ3hoAZ2lgsGkEu0hnlM3XzXcx6taR/L+NGh7r95DBPPQ +79n3y8rDaBcnLvoEgpMUdwKBgQCBN6Qdw1lO8gMHiqP3FqxTs7t4J1sJRC9PU3vd +Dlz6Pt/NGNNf3Y9XaOjYAhQwYhDC57W9fsSyh4NGMMVw8261onS0S0RC56Y7i/0R +h+C/fvUVF+7Td0loedIwH68+PgEkXloGmGJvCWtiwm20eLGuHkH9AHI4Gcxrz8OL +j5NK2QKBgCYKtCqB1rXp4k4KcHYQBSmbIN9aEzd4OqjYtzvrYv8wE8ooRim8dd55 +xRRh1r6Nx7+/KoXg7FgaLbDex4dGeCExjvXLmtJNOj0QwJwID84ZhmzjvL26sY8F +rrt4XPaZv8/5T5kiXE2Mtp3LyIPlq0tRXepdyltrTFPfX3HR+ph2 +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/ca.pem b/tests/tls/ca.pem new file mode 100644 index 0000000..1c05e64 --- /dev/null +++ b/tests/tls/ca.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRDCCAiwCCQC5E5PSm8flUjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGQxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMRkwFwYDVQQDDBBodHRwbGliMi10ZXN0LUNBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrj +mlTHPFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gH +sQn33Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOE +Nh5rlYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0 +QByPPACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwC +PspC+wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABMA0GCSqGSIb3DQEB +CwUAA4IBAQB4b+DWt0An4YoXj7lb/+N7FVr2m5UVyBI+bbEGI/qsql/Ixiaef69M +jej7n5ucUx8GBql62W0c3/E3qZFfo49ngH1WC5gkKQH9V4jGZui5CUfmNE6WepQ/ +vL6eKXUp7RoJ/hWVhGm1uV3OShF+EN0t2wZttYg4lip0FjrY8tRWdjw5yu61wWVu +WuHxTzKiHe9emjhhUBgnWRnNeYPTRs0xM2Awv5KYPq2cmrjGbSz3mYDkBpbiJUp4 +pM9g8qLmsDO2yrlVF659D08+5zkmMbyqnn84X0n3SM3Yn0ayZOmbNHiXoAzklZNP +7xiyxMEAfVQOITsvSDG2PzbZlGGtbaka +-----END CERTIFICATE----- diff --git a/tests/tls/ca.srl b/tests/tls/ca.srl new file mode 100644 index 0000000..ad8d416 --- /dev/null +++ b/tests/tls/ca.srl @@ -0,0 +1 @@ +E2AA6A96D1BF1AEE diff --git a/tests/tls/ca_unused.pem b/tests/tls/ca_unused.pem new file mode 100644 index 0000000..4c4291a --- /dev/null +++ b/tests/tls/ca_unused.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUjCCAjoCCQC47jeQyttLgzANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEgMB4GA1UEAwwXaHR0cGxpYjItdGVzdC1DQS11bnVzZWQw +HhcNMTkwOTI2MTUwMzM0WhcNMjkwOTIzMTUwMzM0WjBrMQswCQYDVQQGEwJaWjEK +MAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVzdDEK +MAgGA1UECwwBLjEgMB4GA1UEAwwXaHR0cGxpYjItdGVzdC1DQS11bnVzZWQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEEp6vCvQzfbfTFYoy/EHbT4BH +1KiNtAg3b/wQj2ltKuOaVMc8VdZ/TarYuXQY9tydIo4ycABGm1UKTER+QMY83TdT +HSOMa7YyvBkdRFczaAexCffcaPccKt4UszEiowV8Z4H1PRXG6uBR6T/5tHzlAFU0 +T0atB+sA3w75Jw6kI4Q2HmuVhz9Cxwf9LRAm+21sYBkU/VsH9eOW3B3p8XetmAJ9 +MnzwHZQhwRW35f+Rz/RAHI88AICcJoThwHar5IlUQxg/oEsNW8I9E7ndQLxyqwt+ +kpTPve3Cj82jg+EwfAI+ykL7AqUAPf4Zaaxq3PStHKybtXQaCEIZ75PkkewbAgMB +AAEwDQYJKoZIhvcNAQELBQADggEBAFbeSPQgXJxfHc1m8wJ4eSW470gXjHZD82uH +sZTj6v+UZlYzVUgDt+KEdZpoIP8C0prhez+scB6YcwiwP5iHfH3AB51jVoQvKAFt +4TNKt9LvOuOzGKk9LmO41xYO6KjAOWuoERdYtBR0h0CyOm756iHwO0bQEELiePfU +hB7o9SlVg0aMcWtbrGBLGBy6HE0p3Oiq/ny0G8r/gshnHvLku6JOxg0XJGDi3LuG +ezBF0HFwK56NaB2syDtQRCT7I5yqLBK2AlwhcbZat07vLFPeDyw4Omh6COJ/tQsU +qIcVJ6kS7VJejjWQD8z5CybYDnmBJJqXW4ixUs8wu0l3miaBdiM= +-----END CERTIFICATE----- diff --git a/tests/tls/client.crt b/tests/tls/client.crt new file mode 100644 index 0000000..9430e27 --- /dev/null +++ b/tests/tls/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjACCQDiqmqW0b8a7DANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGgxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMR0wGwYDVQQDDBRodHRwbGliMi10ZXN0LWNsaWVudDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALtXT//Esar+MDk6Gcj3KDLAyTU7jPqUx4S83LNI +GumuBmOg7oe16SSM//NUFhCHiHr4IzeqDOD2Dq1aM661Ta2EINHvpG+9BEkCWkFr +q4eh0pfocsvU64dtd26TCk9Q4Qqaj4t4HSYYv1hz8UFh5RNLVjgtbR7OQ37Oumcg +jRGnelV/ck6u5BvN/fKK7p/W6hPcO9OjJDAmlVNZVPtP2ki6Lv/Q87x34X+1/Qb1 +LILUOG5mdfCTmf2tYh9bXqZmqoidTY4O7/JiPuT0+1056Ja8bDGYSFXPvvqd1aEW +nGA22MEzd74w9A4tIieCRHlGGOSf0AGsVTmHKRf5bpQjaCsCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAi/X3QjpzPap9IhpHqvgFirsEepruz8lCk+Zo6A/+DP/PocII +/8jWdIV87RDDkkaVGvWOywZyUNN1RAfrt/jGCW8xgCaSGWRab10QIW8DGhbP6FTz +7xcBnQzcoc1gggZBcwOjkRuefW2zkgGIJo5XxHlBfo3T9nX4086Py/b+VoAmcIlm +Y/LNHxtIyDDiOgGK9x7+IqEXQuo/p2z5oFubj/hyNJhXaYU2u7nNMXICYY/eY4vX +GgZ44lGZ2YR7NwzqM5UHNXr7/VJzgxWwAgyZUT8DdnjkZY4wLt1JJas5n3oldBsA ++og2cMk0oOsiFAwHAwE7St4oFY0ivKDhttf7WQ== +-----END CERTIFICATE----- diff --git a/tests/tls/client.key b/tests/tls/client.key new file mode 100644 index 0000000..9a442b8 --- /dev/null +++ b/tests/tls/client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAu1dP/8Sxqv4wOToZyPcoMsDJNTuM+pTHhLzcs0ga6a4GY6Du +h7XpJIz/81QWEIeIevgjN6oM4PYOrVozrrVNrYQg0e+kb70ESQJaQWurh6HSl+hy +y9Trh213bpMKT1DhCpqPi3gdJhi/WHPxQWHlE0tWOC1tHs5Dfs66ZyCNEad6VX9y +Tq7kG8398orun9bqE9w706MkMCaVU1lU+0/aSLou/9DzvHfhf7X9BvUsgtQ4bmZ1 +8JOZ/a1iH1tepmaqiJ1Njg7v8mI+5PT7XTnolrxsMZhIVc+++p3VoRacYDbYwTN3 +vjD0Di0iJ4JEeUYY5J/QAaxVOYcpF/lulCNoKwIDAQABAoIBAQCtzBeUYWauCni0 +bnlDXj91rjI7504nneTm+MsKq6cECQU2YjNHxXRQC1rb47NAjGwKIK+TUXf3L254 +VglCWEKC6eQEvvxfCQyzqrIOpRORlYeok+YDwTjr/5rgAxt6b78GtlLbAYiRMj45 +kf5MOMRqvOZ04XetL4+gUarOR3131L38ysReluabsCXkgIH9kZHmgOW2El4lmoHp +CQpvMkJWyoVZvDbjLi0JoEljHGpfdWNdcllHP4dbNSQtfgG3VXXFgKqWQ8ZiY2U1 +y5SxHaeAjKHBUoGeinox/Myzan3xCysZb+gi5UxcrE7dn5lSB2/AMBymYShI/2qi +UWq64JeJAoGBAPLtHRIcbRn53IhXGwcdFAn7JUSzKvr+gUfJiyfbw28CDWVRgTce +JN+FzTuW92Iwm3ppBKmJ5PcZZnqt7VTtWfLvP126YaGctqZHSWiD9oK3EzDJEWIO +trpMlJkeB+IQlvYMCiC+G+6XFBCdB7X3X1D9Y9z11Kf/arx24bB/ByVlAoGBAMVs +YZRL9idgwgU4LMOqaPkU99de4wzYF44joZrp3Eme3dC9sdHrDtDy6OpiQu4zP+Ax +5cws6M6txd5meAh2YwRhJBmGUYIQuhhNKQjhoeovw0tbtXO9rAHPegXPqg8xwzY9 +Ntc/WlfwM0O7ROfOq4r9erWBn0B7xspxRMH+LIZPAoGBALVW326XnbHYXRHBxEFJ +KZ5Rxf5EqP74YVVPU/uLB5akN4+8ifK1I91fqlajWUQI+Ocl4f8VGsCCS4ekshfF +nnHEus6ixSK5M3dom5nTeH8XXtH6JmnGhg0IAZ1TV5sfuzEsx5qtj3hJewbz0b+6 +S4LPxG47bGWEOw84xzzTdmgpAoGAGj87heTHeBrEEL+UK/tW826XOLnzw7xi/VG9 +ZYQb9mm5ocvmfTscACmbT7X6ogKMRnk7zPZXiUrPGK9U3AMpTObBTudtpLYml56C +ixy8Uw9Ajp9Fs3qPCLqVxXoDaPu7sVVYGivhDfnwRtv54Du40MS8cK8oBgGuvzFp +68SoFL8CgYAY7KvTfTKk4oWWeclmwEoe04woV7J6XuB7OnbxYpKpiGh4juN1E6wo +n9UhAVzO6cAfK/ZuhTkDtvJSsXtQ1xElZLMIG1Yb7yikRyO73EHRUpHon3Gah+79 +MM6uZReiEdkx/hMthL45jP85hfVM89M7LYj9SBoxY2xpuzN+HmiezQ== +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/client.pem b/tests/tls/client.pem new file mode 100644 index 0000000..f12775f --- /dev/null +++ b/tests/tls/client.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjACCQDiqmqW0b8a7DANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGgxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMR0wGwYDVQQDDBRodHRwbGliMi10ZXN0LWNsaWVudDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALtXT//Esar+MDk6Gcj3KDLAyTU7jPqUx4S83LNI +GumuBmOg7oe16SSM//NUFhCHiHr4IzeqDOD2Dq1aM661Ta2EINHvpG+9BEkCWkFr +q4eh0pfocsvU64dtd26TCk9Q4Qqaj4t4HSYYv1hz8UFh5RNLVjgtbR7OQ37Oumcg +jRGnelV/ck6u5BvN/fKK7p/W6hPcO9OjJDAmlVNZVPtP2ki6Lv/Q87x34X+1/Qb1 +LILUOG5mdfCTmf2tYh9bXqZmqoidTY4O7/JiPuT0+1056Ja8bDGYSFXPvvqd1aEW +nGA22MEzd74w9A4tIieCRHlGGOSf0AGsVTmHKRf5bpQjaCsCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAi/X3QjpzPap9IhpHqvgFirsEepruz8lCk+Zo6A/+DP/PocII +/8jWdIV87RDDkkaVGvWOywZyUNN1RAfrt/jGCW8xgCaSGWRab10QIW8DGhbP6FTz +7xcBnQzcoc1gggZBcwOjkRuefW2zkgGIJo5XxHlBfo3T9nX4086Py/b+VoAmcIlm +Y/LNHxtIyDDiOgGK9x7+IqEXQuo/p2z5oFubj/hyNJhXaYU2u7nNMXICYY/eY4vX +GgZ44lGZ2YR7NwzqM5UHNXr7/VJzgxWwAgyZUT8DdnjkZY4wLt1JJas5n3oldBsA ++og2cMk0oOsiFAwHAwE7St4oFY0ivKDhttf7WQ== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAu1dP/8Sxqv4wOToZyPcoMsDJNTuM+pTHhLzcs0ga6a4GY6Du +h7XpJIz/81QWEIeIevgjN6oM4PYOrVozrrVNrYQg0e+kb70ESQJaQWurh6HSl+hy +y9Trh213bpMKT1DhCpqPi3gdJhi/WHPxQWHlE0tWOC1tHs5Dfs66ZyCNEad6VX9y +Tq7kG8398orun9bqE9w706MkMCaVU1lU+0/aSLou/9DzvHfhf7X9BvUsgtQ4bmZ1 +8JOZ/a1iH1tepmaqiJ1Njg7v8mI+5PT7XTnolrxsMZhIVc+++p3VoRacYDbYwTN3 +vjD0Di0iJ4JEeUYY5J/QAaxVOYcpF/lulCNoKwIDAQABAoIBAQCtzBeUYWauCni0 +bnlDXj91rjI7504nneTm+MsKq6cECQU2YjNHxXRQC1rb47NAjGwKIK+TUXf3L254 +VglCWEKC6eQEvvxfCQyzqrIOpRORlYeok+YDwTjr/5rgAxt6b78GtlLbAYiRMj45 +kf5MOMRqvOZ04XetL4+gUarOR3131L38ysReluabsCXkgIH9kZHmgOW2El4lmoHp +CQpvMkJWyoVZvDbjLi0JoEljHGpfdWNdcllHP4dbNSQtfgG3VXXFgKqWQ8ZiY2U1 +y5SxHaeAjKHBUoGeinox/Myzan3xCysZb+gi5UxcrE7dn5lSB2/AMBymYShI/2qi +UWq64JeJAoGBAPLtHRIcbRn53IhXGwcdFAn7JUSzKvr+gUfJiyfbw28CDWVRgTce +JN+FzTuW92Iwm3ppBKmJ5PcZZnqt7VTtWfLvP126YaGctqZHSWiD9oK3EzDJEWIO +trpMlJkeB+IQlvYMCiC+G+6XFBCdB7X3X1D9Y9z11Kf/arx24bB/ByVlAoGBAMVs +YZRL9idgwgU4LMOqaPkU99de4wzYF44joZrp3Eme3dC9sdHrDtDy6OpiQu4zP+Ax +5cws6M6txd5meAh2YwRhJBmGUYIQuhhNKQjhoeovw0tbtXO9rAHPegXPqg8xwzY9 +Ntc/WlfwM0O7ROfOq4r9erWBn0B7xspxRMH+LIZPAoGBALVW326XnbHYXRHBxEFJ +KZ5Rxf5EqP74YVVPU/uLB5akN4+8ifK1I91fqlajWUQI+Ocl4f8VGsCCS4ekshfF +nnHEus6ixSK5M3dom5nTeH8XXtH6JmnGhg0IAZ1TV5sfuzEsx5qtj3hJewbz0b+6 +S4LPxG47bGWEOw84xzzTdmgpAoGAGj87heTHeBrEEL+UK/tW826XOLnzw7xi/VG9 +ZYQb9mm5ocvmfTscACmbT7X6ogKMRnk7zPZXiUrPGK9U3AMpTObBTudtpLYml56C +ixy8Uw9Ajp9Fs3qPCLqVxXoDaPu7sVVYGivhDfnwRtv54Du40MS8cK8oBgGuvzFp +68SoFL8CgYAY7KvTfTKk4oWWeclmwEoe04woV7J6XuB7OnbxYpKpiGh4juN1E6wo +n9UhAVzO6cAfK/ZuhTkDtvJSsXtQ1xElZLMIG1Yb7yikRyO73EHRUpHon3Gah+79 +MM6uZReiEdkx/hMthL45jP85hfVM89M7LYj9SBoxY2xpuzN+HmiezQ== +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/client_chain.pem b/tests/tls/client_chain.pem new file mode 100644 index 0000000..e2427d7 --- /dev/null +++ b/tests/tls/client_chain.pem @@ -0,0 +1,67 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjACCQDiqmqW0b8a7DANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGgxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMR0wGwYDVQQDDBRodHRwbGliMi10ZXN0LWNsaWVudDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALtXT//Esar+MDk6Gcj3KDLAyTU7jPqUx4S83LNI +GumuBmOg7oe16SSM//NUFhCHiHr4IzeqDOD2Dq1aM661Ta2EINHvpG+9BEkCWkFr +q4eh0pfocsvU64dtd26TCk9Q4Qqaj4t4HSYYv1hz8UFh5RNLVjgtbR7OQ37Oumcg +jRGnelV/ck6u5BvN/fKK7p/W6hPcO9OjJDAmlVNZVPtP2ki6Lv/Q87x34X+1/Qb1 +LILUOG5mdfCTmf2tYh9bXqZmqoidTY4O7/JiPuT0+1056Ja8bDGYSFXPvvqd1aEW +nGA22MEzd74w9A4tIieCRHlGGOSf0AGsVTmHKRf5bpQjaCsCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAi/X3QjpzPap9IhpHqvgFirsEepruz8lCk+Zo6A/+DP/PocII +/8jWdIV87RDDkkaVGvWOywZyUNN1RAfrt/jGCW8xgCaSGWRab10QIW8DGhbP6FTz +7xcBnQzcoc1gggZBcwOjkRuefW2zkgGIJo5XxHlBfo3T9nX4086Py/b+VoAmcIlm +Y/LNHxtIyDDiOgGK9x7+IqEXQuo/p2z5oFubj/hyNJhXaYU2u7nNMXICYY/eY4vX +GgZ44lGZ2YR7NwzqM5UHNXr7/VJzgxWwAgyZUT8DdnjkZY4wLt1JJas5n3oldBsA ++og2cMk0oOsiFAwHAwE7St4oFY0ivKDhttf7WQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDRDCCAiwCCQC5E5PSm8flUjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGQxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMRkwFwYDVQQDDBBodHRwbGliMi10ZXN0LUNBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrj +mlTHPFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gH +sQn33Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOE +Nh5rlYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0 +QByPPACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwC +PspC+wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABMA0GCSqGSIb3DQEB +CwUAA4IBAQB4b+DWt0An4YoXj7lb/+N7FVr2m5UVyBI+bbEGI/qsql/Ixiaef69M +jej7n5ucUx8GBql62W0c3/E3qZFfo49ngH1WC5gkKQH9V4jGZui5CUfmNE6WepQ/ +vL6eKXUp7RoJ/hWVhGm1uV3OShF+EN0t2wZttYg4lip0FjrY8tRWdjw5yu61wWVu +WuHxTzKiHe9emjhhUBgnWRnNeYPTRs0xM2Awv5KYPq2cmrjGbSz3mYDkBpbiJUp4 +pM9g8qLmsDO2yrlVF659D08+5zkmMbyqnn84X0n3SM3Yn0ayZOmbNHiXoAzklZNP +7xiyxMEAfVQOITsvSDG2PzbZlGGtbaka +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAu1dP/8Sxqv4wOToZyPcoMsDJNTuM+pTHhLzcs0ga6a4GY6Du +h7XpJIz/81QWEIeIevgjN6oM4PYOrVozrrVNrYQg0e+kb70ESQJaQWurh6HSl+hy +y9Trh213bpMKT1DhCpqPi3gdJhi/WHPxQWHlE0tWOC1tHs5Dfs66ZyCNEad6VX9y +Tq7kG8398orun9bqE9w706MkMCaVU1lU+0/aSLou/9DzvHfhf7X9BvUsgtQ4bmZ1 +8JOZ/a1iH1tepmaqiJ1Njg7v8mI+5PT7XTnolrxsMZhIVc+++p3VoRacYDbYwTN3 +vjD0Di0iJ4JEeUYY5J/QAaxVOYcpF/lulCNoKwIDAQABAoIBAQCtzBeUYWauCni0 +bnlDXj91rjI7504nneTm+MsKq6cECQU2YjNHxXRQC1rb47NAjGwKIK+TUXf3L254 +VglCWEKC6eQEvvxfCQyzqrIOpRORlYeok+YDwTjr/5rgAxt6b78GtlLbAYiRMj45 +kf5MOMRqvOZ04XetL4+gUarOR3131L38ysReluabsCXkgIH9kZHmgOW2El4lmoHp +CQpvMkJWyoVZvDbjLi0JoEljHGpfdWNdcllHP4dbNSQtfgG3VXXFgKqWQ8ZiY2U1 +y5SxHaeAjKHBUoGeinox/Myzan3xCysZb+gi5UxcrE7dn5lSB2/AMBymYShI/2qi +UWq64JeJAoGBAPLtHRIcbRn53IhXGwcdFAn7JUSzKvr+gUfJiyfbw28CDWVRgTce +JN+FzTuW92Iwm3ppBKmJ5PcZZnqt7VTtWfLvP126YaGctqZHSWiD9oK3EzDJEWIO +trpMlJkeB+IQlvYMCiC+G+6XFBCdB7X3X1D9Y9z11Kf/arx24bB/ByVlAoGBAMVs +YZRL9idgwgU4LMOqaPkU99de4wzYF44joZrp3Eme3dC9sdHrDtDy6OpiQu4zP+Ax +5cws6M6txd5meAh2YwRhJBmGUYIQuhhNKQjhoeovw0tbtXO9rAHPegXPqg8xwzY9 +Ntc/WlfwM0O7ROfOq4r9erWBn0B7xspxRMH+LIZPAoGBALVW326XnbHYXRHBxEFJ +KZ5Rxf5EqP74YVVPU/uLB5akN4+8ifK1I91fqlajWUQI+Ocl4f8VGsCCS4ekshfF +nnHEus6ixSK5M3dom5nTeH8XXtH6JmnGhg0IAZ1TV5sfuzEsx5qtj3hJewbz0b+6 +S4LPxG47bGWEOw84xzzTdmgpAoGAGj87heTHeBrEEL+UK/tW826XOLnzw7xi/VG9 +ZYQb9mm5ocvmfTscACmbT7X6ogKMRnk7zPZXiUrPGK9U3AMpTObBTudtpLYml56C +ixy8Uw9Ajp9Fs3qPCLqVxXoDaPu7sVVYGivhDfnwRtv54Du40MS8cK8oBgGuvzFp +68SoFL8CgYAY7KvTfTKk4oWWeclmwEoe04woV7J6XuB7OnbxYpKpiGh4juN1E6wo +n9UhAVzO6cAfK/ZuhTkDtvJSsXtQ1xElZLMIG1Yb7yikRyO73EHRUpHon3Gah+79 +MM6uZReiEdkx/hMthL45jP85hfVM89M7LYj9SBoxY2xpuzN+HmiezQ== +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/client_encrypted.crt b/tests/tls/client_encrypted.crt new file mode 100644 index 0000000..6301dd5 --- /dev/null +++ b/tests/tls/client_encrypted.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTDCCAjQCCQDiqmqW0b8a7TANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGwxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMSEwHwYDVQQDDBhodHRwbGliMi10ZXN0LWNsaWVudC1lbmMwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V0//xLGq/jA5OhnI9ygywMk1O4z6lMeE +vNyzSBrprgZjoO6HtekkjP/zVBYQh4h6+CM3qgzg9g6tWjOutU2thCDR76RvvQRJ +AlpBa6uHodKX6HLL1OuHbXdukwpPUOEKmo+LeB0mGL9Yc/FBYeUTS1Y4LW0ezkN+ +zrpnII0Rp3pVf3JOruQbzf3yiu6f1uoT3DvToyQwJpVTWVT7T9pIui7/0PO8d+F/ +tf0G9SyC1DhuZnXwk5n9rWIfW16mZqqInU2ODu/yYj7k9PtdOeiWvGwxmEhVz776 +ndWhFpxgNtjBM3e+MPQOLSIngkR5Rhjkn9ABrFU5hykX+W6UI2grAgMBAAEwDQYJ +KoZIhvcNAQEFBQADggEBAKiBTMX/FwUusM4PIsmGqXisOBo6LEf2YtfzQrtxw4eY +eWeKsi3aM2GquCqh0R7loEW+yQoxPEBaNeOBeN3v8sdhTu+9NjK31tWCYr7jvEa5 +TqjlUUMD1176YBQ8axI51lVcaBIoRdvf8nXm7idvp82eBBXQtnREjd8oKcEz7v4x +ECJ+RWGJTEIWXq3fuVvBAJeopNVz+Utt61DCxziKbu+ndv0kQeXZ7KPFiBnARcEi +7GvTeHUA0cbpHrNY0ob7ozcjGiPwW5HPi+DYZYfRm2PqI9vowmKt9By+8Uz03K3L +XMZkGJ28uoo37Rbjs8+pMVDdHoUrm6hZTkw5XGgsA6I= +-----END CERTIFICATE----- diff --git a/tests/tls/client_encrypted.key b/tests/tls/client_encrypted.key new file mode 100644 index 0000000..747b1fb --- /dev/null +++ b/tests/tls/client_encrypted.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,84B979083590A865DF5700455C62AEAF + +Q2vQ9vhIV6C3aL4Uk9XIqtsgdN9VN+Ep3mDY7FKbycrcZgDnXbZ5ys50cF8TyJZx +ITYV5ms9MjjUi1cj3hhUfQSu1bJ+gKGWpxHD1Cxub9XLcGJrjfGomO0r5lNe4YZf +of4qc+XpoY+dZTHyFuKn44LiynAG0esqexNc7BcqbGSw6cjm6exvDWbmz0svy2RO +TTfea6oh2+CiHUWqIbjIEoK/tJXXzUq9LwEMGdvyQX5KJDLmsWdxejnqsMmSDBH4 +Bm5ax7AIcO4SE1AdYzzDNI11bZXyQu1UWtgL3D4sDo4iwbHzq9CFXSI21tgwW3WT +LK3Pbt6IAJctwNZozwWDT9c0r226WZv/lYk4bkhvGDuLFMgMwjmW24i4VuU7giiv +sna6jJNk0gQ0XCWiIgtjhrH8noefj07SPb5miXQjhbCvz3F5TqGobWS6xSCX+KMF +LfjJdG5f6QHfnzm+fmIuc/JdcHMCf/2q3bvHuTOoYoJXMY9kcKS5PXckADsQkzmM +2AeoegO8x25ClHRBSOgwee9tkrD5VTX3uK92/rnnt3KUiS8Rdf6jbYpJg2rvCTAd +NF6GCCp+YNw3o7a5IehhhwXWKh0BK02e6IPU9Z6KFTX8LGFOPy8BIghyNILPpfeC +Ir2WWr8asfBLvwy5Pj6Wzfvu6EPC6H0RIxzhTTfaZ0KZR3ZN1Fwd49FNql50UeHH +M5x1vzi4JuRak4z22+uL3GEdkBs/PAoVMfctHTMEiDrH3mgYrEj5yhCgUjJhMP6w +dk6gUaXcPZ6JymUtrZsjcc3cXDD6Vn6i1SH2e9rMf9/QBKGZH3ufkBJ9s0HVsacq +bHg63+BGhmLXFcnEimGVY86py8TfQfuirFOEURoYJMUTCl046GV11AJeF0L+EOUH +nst2Z9l86vCH+7sdVPsVG8jFaeOunr50VkCu/ephGsUSHjNRVagsVOSojknMyNki +D13Oolykusq9zuHMBBRceNRoAHLSk02aHiuwlvQmAVaSJxIebtFZJmG1asXMSK/M +iYjLNb7P+fjpvxYwk1bxXTyTToHLsmvDeGyMj3EhrEh83o/Xoo+bac7OW+l18wWw +N5b5Jcrj0e/O08+dJ5UBXGJ36V5WENTdOjctFTRtZhggkvB1QBZLa1g9khxz0VEw +q0yGYCmjMK2YxpJhH+FAprUM+0Ei9IxIjDY295CEOrKkeJGb9+A8CWLoCi8z0zAc +vQU/uaQsK1BKamKGmu7x+xCyoLSprzNlPjipJttUqkE00kFX9sBJZXW/rI+fQRJI +sYn9UqNp/ay00uswwcoFLAnX7YdKiJrB5jaR5oyLFXpIPQa76594iRcf+9XkD9Ca +KlscDNVnfSW7bkp7LJ+Rm6+OKWCSjglL8uIyBEzoNFCyiEnnzebE+jwvVBaDlbPd +xMwMzU8vHsb9dYd7RMdd+YBIxngzyJiVl6Zpfy9B74vhL+ndyWKZg2Rsunlrcms7 +YIVg/LuAfgH4jXg38yzHHEkArGZg5TFaGUD8rJwMIPil6LOQ4D+jK8/fcV9bhBuH +LzBJ4gtPwUnvYqsaiIAeGi2EVllW0Ka+aTTzM1Yascl2q9WROvutAT0zz0M6smpO +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/client_encrypted.pem b/tests/tls/client_encrypted.pem new file mode 100644 index 0000000..578aa7f --- /dev/null +++ b/tests/tls/client_encrypted.pem @@ -0,0 +1,50 @@ +-----BEGIN CERTIFICATE----- +MIIDTDCCAjQCCQDiqmqW0b8a7TANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGwxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMSEwHwYDVQQDDBhodHRwbGliMi10ZXN0LWNsaWVudC1lbmMwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V0//xLGq/jA5OhnI9ygywMk1O4z6lMeE +vNyzSBrprgZjoO6HtekkjP/zVBYQh4h6+CM3qgzg9g6tWjOutU2thCDR76RvvQRJ +AlpBa6uHodKX6HLL1OuHbXdukwpPUOEKmo+LeB0mGL9Yc/FBYeUTS1Y4LW0ezkN+ +zrpnII0Rp3pVf3JOruQbzf3yiu6f1uoT3DvToyQwJpVTWVT7T9pIui7/0PO8d+F/ +tf0G9SyC1DhuZnXwk5n9rWIfW16mZqqInU2ODu/yYj7k9PtdOeiWvGwxmEhVz776 +ndWhFpxgNtjBM3e+MPQOLSIngkR5Rhjkn9ABrFU5hykX+W6UI2grAgMBAAEwDQYJ +KoZIhvcNAQEFBQADggEBAKiBTMX/FwUusM4PIsmGqXisOBo6LEf2YtfzQrtxw4eY +eWeKsi3aM2GquCqh0R7loEW+yQoxPEBaNeOBeN3v8sdhTu+9NjK31tWCYr7jvEa5 +TqjlUUMD1176YBQ8axI51lVcaBIoRdvf8nXm7idvp82eBBXQtnREjd8oKcEz7v4x +ECJ+RWGJTEIWXq3fuVvBAJeopNVz+Utt61DCxziKbu+ndv0kQeXZ7KPFiBnARcEi +7GvTeHUA0cbpHrNY0ob7ozcjGiPwW5HPi+DYZYfRm2PqI9vowmKt9By+8Uz03K3L +XMZkGJ28uoo37Rbjs8+pMVDdHoUrm6hZTkw5XGgsA6I= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,84B979083590A865DF5700455C62AEAF + +Q2vQ9vhIV6C3aL4Uk9XIqtsgdN9VN+Ep3mDY7FKbycrcZgDnXbZ5ys50cF8TyJZx +ITYV5ms9MjjUi1cj3hhUfQSu1bJ+gKGWpxHD1Cxub9XLcGJrjfGomO0r5lNe4YZf +of4qc+XpoY+dZTHyFuKn44LiynAG0esqexNc7BcqbGSw6cjm6exvDWbmz0svy2RO +TTfea6oh2+CiHUWqIbjIEoK/tJXXzUq9LwEMGdvyQX5KJDLmsWdxejnqsMmSDBH4 +Bm5ax7AIcO4SE1AdYzzDNI11bZXyQu1UWtgL3D4sDo4iwbHzq9CFXSI21tgwW3WT +LK3Pbt6IAJctwNZozwWDT9c0r226WZv/lYk4bkhvGDuLFMgMwjmW24i4VuU7giiv +sna6jJNk0gQ0XCWiIgtjhrH8noefj07SPb5miXQjhbCvz3F5TqGobWS6xSCX+KMF +LfjJdG5f6QHfnzm+fmIuc/JdcHMCf/2q3bvHuTOoYoJXMY9kcKS5PXckADsQkzmM +2AeoegO8x25ClHRBSOgwee9tkrD5VTX3uK92/rnnt3KUiS8Rdf6jbYpJg2rvCTAd +NF6GCCp+YNw3o7a5IehhhwXWKh0BK02e6IPU9Z6KFTX8LGFOPy8BIghyNILPpfeC +Ir2WWr8asfBLvwy5Pj6Wzfvu6EPC6H0RIxzhTTfaZ0KZR3ZN1Fwd49FNql50UeHH +M5x1vzi4JuRak4z22+uL3GEdkBs/PAoVMfctHTMEiDrH3mgYrEj5yhCgUjJhMP6w +dk6gUaXcPZ6JymUtrZsjcc3cXDD6Vn6i1SH2e9rMf9/QBKGZH3ufkBJ9s0HVsacq +bHg63+BGhmLXFcnEimGVY86py8TfQfuirFOEURoYJMUTCl046GV11AJeF0L+EOUH +nst2Z9l86vCH+7sdVPsVG8jFaeOunr50VkCu/ephGsUSHjNRVagsVOSojknMyNki +D13Oolykusq9zuHMBBRceNRoAHLSk02aHiuwlvQmAVaSJxIebtFZJmG1asXMSK/M +iYjLNb7P+fjpvxYwk1bxXTyTToHLsmvDeGyMj3EhrEh83o/Xoo+bac7OW+l18wWw +N5b5Jcrj0e/O08+dJ5UBXGJ36V5WENTdOjctFTRtZhggkvB1QBZLa1g9khxz0VEw +q0yGYCmjMK2YxpJhH+FAprUM+0Ei9IxIjDY295CEOrKkeJGb9+A8CWLoCi8z0zAc +vQU/uaQsK1BKamKGmu7x+xCyoLSprzNlPjipJttUqkE00kFX9sBJZXW/rI+fQRJI +sYn9UqNp/ay00uswwcoFLAnX7YdKiJrB5jaR5oyLFXpIPQa76594iRcf+9XkD9Ca +KlscDNVnfSW7bkp7LJ+Rm6+OKWCSjglL8uIyBEzoNFCyiEnnzebE+jwvVBaDlbPd +xMwMzU8vHsb9dYd7RMdd+YBIxngzyJiVl6Zpfy9B74vhL+ndyWKZg2Rsunlrcms7 +YIVg/LuAfgH4jXg38yzHHEkArGZg5TFaGUD8rJwMIPil6LOQ4D+jK8/fcV9bhBuH +LzBJ4gtPwUnvYqsaiIAeGi2EVllW0Ka+aTTzM1Yascl2q9WROvutAT0zz0M6smpO +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/server.crt b/tests/tls/server.crt new file mode 100644 index 0000000..e29537a --- /dev/null +++ b/tests/tls/server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPTCCAiUCCQDiqmqW0b8a7jANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMF0xCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCu9WJEGBARCdyqt+/ip53p+S7N0lKGv0w75LfqVldIQGYMAf9xSMcb +PSTds9rYq8g7XaehNJ628w0hWnHDEU1x28ULMlL22p91inEmWANyi+flCopgUgPe +sQkc+kcXUVv+/Of8eclIXCxni67J8fK7nH/48ML+qdWes9nrtMRB5z/2mC7sc53x +4faJdOpCx13CaHvYPIHJfT7d7haTDBGLVCSC90tB5WvK7E1hIoIddqwrthWopRcS +iSmQfJZmKPgPTtnVX1r3meLEVcdLcZ9OO8r1buUrBdB5Z25K5r0nCYzAk4AyJ1pq +8tjajMwsunbLFOQ6X9DNRCVOG0XDpu0ZAgMBAAEwDQYJKoZIhvcNAQEFBQADggEB +AGVLrbXSANOMZpWevPJUMoZMJ4H26q9+tJ4kVi36ufQOaRBJ19lc73wt/5CL1Zzx +uHBWJSMkUXn3CIXjyV9QsP3YF94yG9LghIKI/0Z+DK5j1TomplS13DZ+JuUIbogZ +gwTXy/EuAxynUO+iyLD3c7/rJO94luWd2Ct9ljv9Kza7LxeEjKloBeGxddWgeU7/ +OdkPzLmvCxAsK/Wk4LAKG0p3ZwIqLdusMl6TBpStntLhh98M5xQXoozmRo8bBlp5 +kjItngdSWKWyXalw93SGEoPhe7u6fAxMBBuEpAtF5DS+mzTHB/wbJz1FuD3f973J +4MeDFTIHtkTp/lYrbSmWvzU= +-----END CERTIFICATE----- diff --git a/tests/tls/server.key b/tests/tls/server.key new file mode 100644 index 0000000..9a1f1dd --- /dev/null +++ b/tests/tls/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArvViRBgQEQncqrfv4qed6fkuzdJShr9MO+S36lZXSEBmDAH/ +cUjHGz0k3bPa2KvIO12noTSetvMNIVpxwxFNcdvFCzJS9tqfdYpxJlgDcovn5QqK +YFID3rEJHPpHF1Fb/vzn/HnJSFwsZ4uuyfHyu5x/+PDC/qnVnrPZ67TEQec/9pgu +7HOd8eH2iXTqQsddwmh72DyByX0+3e4WkwwRi1QkgvdLQeVryuxNYSKCHXasK7YV +qKUXEokpkHyWZij4D07Z1V9a95nixFXHS3GfTjvK9W7lKwXQeWduSua9JwmMwJOA +MidaavLY2ozMLLp2yxTkOl/QzUQlThtFw6btGQIDAQABAoIBAFyq69lVRW1A4/go +ZI6QaTu8F+Y8OCnWuPIgOqmMAb7rHSHPDRVbjtoGkLg8wvVwRyXqfRcNX+NW6OV5 +mjfPuk1MMhm0Fe1Z7ou7QCMnCuxo3fKamqBZ0GLrMgB/L5hSJ3/vRJCdkNcauwo9 +Gd8sn3xvb/jSzPVFzze32vzVSf39NSxcLRGnMb1Y/1U5jA7Bc+XbPb1fvWJ9ZJec +o5wz0VLNWgYEt3dnvWZOf1ONx50ROwlKFOPhdYg/IQxzkWxOu6ZcleJrJsf5eCvj +o2Ogm6bWMhTO73MYjHD0+hR6xl3g/vIBe+N4e4LmnI7BBQRe9SKrazqBt35/5Zs5 +IKFDPAECgYEA5M2sI7dnbHK1Ur+AIGEan3sWtbXOvg0xihCXRojE5oSKC5BFf5ny +LvZ/VcszhjFv6ruUunXtN8qXJ8n3QQQXyuEeKpkGCvJqE8inREKSTexLj06niU1r +w4XyyEvIckz42sv2k0mwH7qCIwAMkAQtqnM/ue2aCuyKKP5OLqqlSskCgYEAw8E/ +3zWbLwtWb6VtyaSv9/xHL71SXkxpY9FIc7MFhQLnvR6ZgZS42fXopeo04Byx6cZ3 +3QM8UPYE77H0ch3r1HdZIIXLw2aX8SnZtHtchU50cdmhCV51D6dk/bftxgncFzxp +nHRwoPhJTXF/y0+jNP8m5Tn5YgxzJ9Sec7WuZ9ECgYEAnFqURMADNA/bKxXkR7wz +xkIGDdyU0DkR3mhiB/hUnbZ641YOuBkKb99Qut8mcZB9C2puQ1Fs7tBJpQ4WId7b +J2/Y/oEdqQNpS+W1sCbR9eAA7ohwYpp+htmVRBzNeJZzBImXEaWsbrI0VhilfRDt +5+nj5Xmh588mxsapxKgmVkkCgYEAhJiaEy/UdgFQA0AjJbsQFwIjlgq/iHBp0tso +IHba/kYBgvD/Oe7rZ3hSplAGkOfe+2McPfC7InwCy/nWgpYR8FEHZig65ZjQwuJ+ +POpyuTlzVsr7ccUxtfDFT7cOsF5tXq/lOb0FrYOA45xF3AmNm5BZYFvsuKWGOyyi +R+6AvIECgYAXQ8Ud5GX0aXm8cRbwLStamooBreeCKQ9plLnXdqiwkjoeqhdHcWzh +M4Cws86fbRSWESqvY3NVJCA5Na9HN0/LH/UlxN8tfrEfv1al/2UXN8zIL1a1uVfK +H9CtL9znc7mJKBODBxDXgdC+QHMdtGwGU5QYTVwlPEBbdM/2JwgBfw== +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/server.pem b/tests/tls/server.pem new file mode 100644 index 0000000..83277c1 --- /dev/null +++ b/tests/tls/server.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIDPTCCAiUCCQDiqmqW0b8a7jANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMF0xCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCu9WJEGBARCdyqt+/ip53p+S7N0lKGv0w75LfqVldIQGYMAf9xSMcb +PSTds9rYq8g7XaehNJ628w0hWnHDEU1x28ULMlL22p91inEmWANyi+flCopgUgPe +sQkc+kcXUVv+/Of8eclIXCxni67J8fK7nH/48ML+qdWes9nrtMRB5z/2mC7sc53x +4faJdOpCx13CaHvYPIHJfT7d7haTDBGLVCSC90tB5WvK7E1hIoIddqwrthWopRcS +iSmQfJZmKPgPTtnVX1r3meLEVcdLcZ9OO8r1buUrBdB5Z25K5r0nCYzAk4AyJ1pq +8tjajMwsunbLFOQ6X9DNRCVOG0XDpu0ZAgMBAAEwDQYJKoZIhvcNAQEFBQADggEB +AGVLrbXSANOMZpWevPJUMoZMJ4H26q9+tJ4kVi36ufQOaRBJ19lc73wt/5CL1Zzx +uHBWJSMkUXn3CIXjyV9QsP3YF94yG9LghIKI/0Z+DK5j1TomplS13DZ+JuUIbogZ +gwTXy/EuAxynUO+iyLD3c7/rJO94luWd2Ct9ljv9Kza7LxeEjKloBeGxddWgeU7/ +OdkPzLmvCxAsK/Wk4LAKG0p3ZwIqLdusMl6TBpStntLhh98M5xQXoozmRo8bBlp5 +kjItngdSWKWyXalw93SGEoPhe7u6fAxMBBuEpAtF5DS+mzTHB/wbJz1FuD3f973J +4MeDFTIHtkTp/lYrbSmWvzU= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArvViRBgQEQncqrfv4qed6fkuzdJShr9MO+S36lZXSEBmDAH/ +cUjHGz0k3bPa2KvIO12noTSetvMNIVpxwxFNcdvFCzJS9tqfdYpxJlgDcovn5QqK +YFID3rEJHPpHF1Fb/vzn/HnJSFwsZ4uuyfHyu5x/+PDC/qnVnrPZ67TEQec/9pgu +7HOd8eH2iXTqQsddwmh72DyByX0+3e4WkwwRi1QkgvdLQeVryuxNYSKCHXasK7YV +qKUXEokpkHyWZij4D07Z1V9a95nixFXHS3GfTjvK9W7lKwXQeWduSua9JwmMwJOA +MidaavLY2ozMLLp2yxTkOl/QzUQlThtFw6btGQIDAQABAoIBAFyq69lVRW1A4/go +ZI6QaTu8F+Y8OCnWuPIgOqmMAb7rHSHPDRVbjtoGkLg8wvVwRyXqfRcNX+NW6OV5 +mjfPuk1MMhm0Fe1Z7ou7QCMnCuxo3fKamqBZ0GLrMgB/L5hSJ3/vRJCdkNcauwo9 +Gd8sn3xvb/jSzPVFzze32vzVSf39NSxcLRGnMb1Y/1U5jA7Bc+XbPb1fvWJ9ZJec +o5wz0VLNWgYEt3dnvWZOf1ONx50ROwlKFOPhdYg/IQxzkWxOu6ZcleJrJsf5eCvj +o2Ogm6bWMhTO73MYjHD0+hR6xl3g/vIBe+N4e4LmnI7BBQRe9SKrazqBt35/5Zs5 +IKFDPAECgYEA5M2sI7dnbHK1Ur+AIGEan3sWtbXOvg0xihCXRojE5oSKC5BFf5ny +LvZ/VcszhjFv6ruUunXtN8qXJ8n3QQQXyuEeKpkGCvJqE8inREKSTexLj06niU1r +w4XyyEvIckz42sv2k0mwH7qCIwAMkAQtqnM/ue2aCuyKKP5OLqqlSskCgYEAw8E/ +3zWbLwtWb6VtyaSv9/xHL71SXkxpY9FIc7MFhQLnvR6ZgZS42fXopeo04Byx6cZ3 +3QM8UPYE77H0ch3r1HdZIIXLw2aX8SnZtHtchU50cdmhCV51D6dk/bftxgncFzxp +nHRwoPhJTXF/y0+jNP8m5Tn5YgxzJ9Sec7WuZ9ECgYEAnFqURMADNA/bKxXkR7wz +xkIGDdyU0DkR3mhiB/hUnbZ641YOuBkKb99Qut8mcZB9C2puQ1Fs7tBJpQ4WId7b +J2/Y/oEdqQNpS+W1sCbR9eAA7ohwYpp+htmVRBzNeJZzBImXEaWsbrI0VhilfRDt +5+nj5Xmh588mxsapxKgmVkkCgYEAhJiaEy/UdgFQA0AjJbsQFwIjlgq/iHBp0tso +IHba/kYBgvD/Oe7rZ3hSplAGkOfe+2McPfC7InwCy/nWgpYR8FEHZig65ZjQwuJ+ +POpyuTlzVsr7ccUxtfDFT7cOsF5tXq/lOb0FrYOA45xF3AmNm5BZYFvsuKWGOyyi +R+6AvIECgYAXQ8Ud5GX0aXm8cRbwLStamooBreeCKQ9plLnXdqiwkjoeqhdHcWzh +M4Cws86fbRSWESqvY3NVJCA5Na9HN0/LH/UlxN8tfrEfv1al/2UXN8zIL1a1uVfK +H9CtL9znc7mJKBODBxDXgdC+QHMdtGwGU5QYTVwlPEBbdM/2JwgBfw== +-----END RSA PRIVATE KEY----- diff --git a/tests/tls/server_chain.pem b/tests/tls/server_chain.pem new file mode 100644 index 0000000..d590275 --- /dev/null +++ b/tests/tls/server_chain.pem @@ -0,0 +1,67 @@ +-----BEGIN CERTIFICATE----- +MIIDPTCCAiUCCQDiqmqW0b8a7jANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMF0xCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCu9WJEGBARCdyqt+/ip53p+S7N0lKGv0w75LfqVldIQGYMAf9xSMcb +PSTds9rYq8g7XaehNJ628w0hWnHDEU1x28ULMlL22p91inEmWANyi+flCopgUgPe +sQkc+kcXUVv+/Of8eclIXCxni67J8fK7nH/48ML+qdWes9nrtMRB5z/2mC7sc53x +4faJdOpCx13CaHvYPIHJfT7d7haTDBGLVCSC90tB5WvK7E1hIoIddqwrthWopRcS +iSmQfJZmKPgPTtnVX1r3meLEVcdLcZ9OO8r1buUrBdB5Z25K5r0nCYzAk4AyJ1pq +8tjajMwsunbLFOQ6X9DNRCVOG0XDpu0ZAgMBAAEwDQYJKoZIhvcNAQEFBQADggEB +AGVLrbXSANOMZpWevPJUMoZMJ4H26q9+tJ4kVi36ufQOaRBJ19lc73wt/5CL1Zzx +uHBWJSMkUXn3CIXjyV9QsP3YF94yG9LghIKI/0Z+DK5j1TomplS13DZ+JuUIbogZ +gwTXy/EuAxynUO+iyLD3c7/rJO94luWd2Ct9ljv9Kza7LxeEjKloBeGxddWgeU7/ +OdkPzLmvCxAsK/Wk4LAKG0p3ZwIqLdusMl6TBpStntLhh98M5xQXoozmRo8bBlp5 +kjItngdSWKWyXalw93SGEoPhe7u6fAxMBBuEpAtF5DS+mzTHB/wbJz1FuD3f973J +4MeDFTIHtkTp/lYrbSmWvzU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDRDCCAiwCCQC5E5PSm8flUjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJa +WjEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEWMBQGA1UECgwNaHR0cGxpYjItdGVz +dDEKMAgGA1UECwwBLjEZMBcGA1UEAwwQaHR0cGxpYjItdGVzdC1DQTAeFw0xOTA5 +MjYxNTAzMzRaFw0yOTA5MjMxNTAzMzRaMGQxCzAJBgNVBAYTAlpaMQowCAYDVQQI +DAEuMQowCAYDVQQHDAEuMRYwFAYDVQQKDA1odHRwbGliMi10ZXN0MQowCAYDVQQL +DAEuMRkwFwYDVQQDDBBodHRwbGliMi10ZXN0LUNBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAxBKerwr0M3230xWKMvxB20+AR9SojbQIN2/8EI9pbSrj +mlTHPFXWf02q2Ll0GPbcnSKOMnAARptVCkxEfkDGPN03Ux0jjGu2MrwZHURXM2gH +sQn33Gj3HCreFLMxIqMFfGeB9T0VxurgUek/+bR85QBVNE9GrQfrAN8O+ScOpCOE +Nh5rlYc/QscH/S0QJvttbGAZFP1bB/Xjltwd6fF3rZgCfTJ88B2UIcEVt+X/kc/0 +QByPPACAnCaE4cB2q+SJVEMYP6BLDVvCPRO53UC8cqsLfpKUz73two/No4PhMHwC +PspC+wKlAD3+GWmsatz0rRysm7V0GghCGe+T5JHsGwIDAQABMA0GCSqGSIb3DQEB +CwUAA4IBAQB4b+DWt0An4YoXj7lb/+N7FVr2m5UVyBI+bbEGI/qsql/Ixiaef69M +jej7n5ucUx8GBql62W0c3/E3qZFfo49ngH1WC5gkKQH9V4jGZui5CUfmNE6WepQ/ +vL6eKXUp7RoJ/hWVhGm1uV3OShF+EN0t2wZttYg4lip0FjrY8tRWdjw5yu61wWVu +WuHxTzKiHe9emjhhUBgnWRnNeYPTRs0xM2Awv5KYPq2cmrjGbSz3mYDkBpbiJUp4 +pM9g8qLmsDO2yrlVF659D08+5zkmMbyqnn84X0n3SM3Yn0ayZOmbNHiXoAzklZNP +7xiyxMEAfVQOITsvSDG2PzbZlGGtbaka +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArvViRBgQEQncqrfv4qed6fkuzdJShr9MO+S36lZXSEBmDAH/ +cUjHGz0k3bPa2KvIO12noTSetvMNIVpxwxFNcdvFCzJS9tqfdYpxJlgDcovn5QqK +YFID3rEJHPpHF1Fb/vzn/HnJSFwsZ4uuyfHyu5x/+PDC/qnVnrPZ67TEQec/9pgu +7HOd8eH2iXTqQsddwmh72DyByX0+3e4WkwwRi1QkgvdLQeVryuxNYSKCHXasK7YV +qKUXEokpkHyWZij4D07Z1V9a95nixFXHS3GfTjvK9W7lKwXQeWduSua9JwmMwJOA +MidaavLY2ozMLLp2yxTkOl/QzUQlThtFw6btGQIDAQABAoIBAFyq69lVRW1A4/go +ZI6QaTu8F+Y8OCnWuPIgOqmMAb7rHSHPDRVbjtoGkLg8wvVwRyXqfRcNX+NW6OV5 +mjfPuk1MMhm0Fe1Z7ou7QCMnCuxo3fKamqBZ0GLrMgB/L5hSJ3/vRJCdkNcauwo9 +Gd8sn3xvb/jSzPVFzze32vzVSf39NSxcLRGnMb1Y/1U5jA7Bc+XbPb1fvWJ9ZJec +o5wz0VLNWgYEt3dnvWZOf1ONx50ROwlKFOPhdYg/IQxzkWxOu6ZcleJrJsf5eCvj +o2Ogm6bWMhTO73MYjHD0+hR6xl3g/vIBe+N4e4LmnI7BBQRe9SKrazqBt35/5Zs5 +IKFDPAECgYEA5M2sI7dnbHK1Ur+AIGEan3sWtbXOvg0xihCXRojE5oSKC5BFf5ny +LvZ/VcszhjFv6ruUunXtN8qXJ8n3QQQXyuEeKpkGCvJqE8inREKSTexLj06niU1r +w4XyyEvIckz42sv2k0mwH7qCIwAMkAQtqnM/ue2aCuyKKP5OLqqlSskCgYEAw8E/ +3zWbLwtWb6VtyaSv9/xHL71SXkxpY9FIc7MFhQLnvR6ZgZS42fXopeo04Byx6cZ3 +3QM8UPYE77H0ch3r1HdZIIXLw2aX8SnZtHtchU50cdmhCV51D6dk/bftxgncFzxp +nHRwoPhJTXF/y0+jNP8m5Tn5YgxzJ9Sec7WuZ9ECgYEAnFqURMADNA/bKxXkR7wz +xkIGDdyU0DkR3mhiB/hUnbZ641YOuBkKb99Qut8mcZB9C2puQ1Fs7tBJpQ4WId7b +J2/Y/oEdqQNpS+W1sCbR9eAA7ohwYpp+htmVRBzNeJZzBImXEaWsbrI0VhilfRDt +5+nj5Xmh588mxsapxKgmVkkCgYEAhJiaEy/UdgFQA0AjJbsQFwIjlgq/iHBp0tso +IHba/kYBgvD/Oe7rZ3hSplAGkOfe+2McPfC7InwCy/nWgpYR8FEHZig65ZjQwuJ+ +POpyuTlzVsr7ccUxtfDFT7cOsF5tXq/lOb0FrYOA45xF3AmNm5BZYFvsuKWGOyyi +R+6AvIECgYAXQ8Ud5GX0aXm8cRbwLStamooBreeCKQ9plLnXdqiwkjoeqhdHcWzh +M4Cws86fbRSWESqvY3NVJCA5Na9HN0/LH/UlxN8tfrEfv1al/2UXN8zIL1a1uVfK +H9CtL9znc7mJKBODBxDXgdC+QHMdtGwGU5QYTVwlPEBbdM/2JwgBfw== +-----END RSA PRIVATE KEY----- -- cgit v1.2.3 From 4009e8e4922bb592f6ab93cb1f3aa0e4d650e847 Mon Sep 17 00:00:00 2001 From: Konstantin Koshelev <54158304+kkoshelev-g@users.noreply.github.com> Date: Mon, 21 Oct 2019 07:28:11 -0700 Subject: add support for password protected certificate files https://github.com/httplib2/httplib2/pull/143 --- python2/httplib2/__init__.py | 44 +++++++++++++++++++++++++++++++++++--------- python3/httplib2/__init__.py | 23 ++++++++++++++++------- tests/test_https.py | 24 ++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py index 98228e3..d807bc1 100644 --- a/python2/httplib2/__init__.py +++ b/python2/httplib2/__init__.py @@ -76,7 +76,7 @@ if ssl is not None: def _ssl_wrap_socket( - sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname + sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname, key_password ): if disable_validation: cert_reqs = ssl.CERT_NONE @@ -90,11 +90,16 @@ def _ssl_wrap_socket( context.verify_mode = cert_reqs context.check_hostname = cert_reqs != ssl.CERT_NONE if cert_file: - context.load_cert_chain(cert_file, key_file) + if key_password: + context.load_cert_chain(cert_file, key_file, key_password) + else: + context.load_cert_chain(cert_file, key_file) if ca_certs: context.load_verify_locations(ca_certs) return context.wrap_socket(sock, server_hostname=hostname) else: + if key_password: + raise NotSupportedOnThisPlatform("Certificate with password is not supported.") return ssl.wrap_socket( sock, keyfile=key_file, @@ -106,7 +111,7 @@ def _ssl_wrap_socket( def _ssl_wrap_socket_unsupported( - sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname + sock, key_file, cert_file, disable_validation, ca_certs, ssl_version, hostname, key_password ): if not disable_validation: raise CertificateValidationUnsupported( @@ -114,6 +119,8 @@ def _ssl_wrap_socket_unsupported( "the ssl module installed. To avoid this error, install " "the ssl module, or explicity disable validation." ) + if key_password: + raise NotSupportedOnThisPlatform("Certificate with password is not supported.") ssl_sock = socket.ssl(sock, key_file, cert_file) return httplib.FakeSocket(sock, ssl_sock) @@ -978,8 +985,13 @@ class Credentials(object): class KeyCerts(Credentials): """Identical to Credentials except that name/password are mapped to key/cert.""" + def add(self, key, cert, domain, password): + self.credentials.append((domain.lower(), key, cert, password)) - pass + def iter(self, domain): + for (cdomain, key, cert, password) in self.credentials: + if cdomain == "" or domain == cdomain: + yield (key, cert, password) class AllHosts(object): @@ -1253,10 +1265,19 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): ca_certs=None, disable_ssl_certificate_validation=False, ssl_version=None, + key_password=None, ): - httplib.HTTPSConnection.__init__( - self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict - ) + if key_password: + httplib.HTTPSConnection.__init__(self, host, port=port, strict=strict) + self._context.load_cert_chain(cert_file, key_file, key_password) + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password + else: + httplib.HTTPSConnection.__init__( + self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict + ) + self.key_password = None self.timeout = timeout self.proxy_info = proxy_info if ca_certs is None: @@ -1366,6 +1387,7 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): self.ca_certs, self.ssl_version, self.host, + self.key_password, ) if self.debuglevel > 0: print("connect: (%s, %s)" % (self.host, self.port)) @@ -1515,7 +1537,10 @@ class AppEngineHttpsConnection(httplib.HTTPSConnection): ca_certs=None, disable_ssl_certificate_validation=False, ssl_version=None, + key_password=None, ): + if key_password: + raise NotSupportedOnThisPlatform("Certificate with password is not supported.") httplib.HTTPSConnection.__init__( self, host, @@ -1680,10 +1705,10 @@ class Http(object): any time a request requires authentication.""" self.credentials.add(name, password, domain) - def add_certificate(self, key, cert, domain): + def add_certificate(self, key, cert, domain, password=None): """Add a key and cert that will be used any time a request requires authentication.""" - self.certificates.add(key, cert, domain) + self.certificates.add(key, cert, domain, password) def clear_credentials(self): """Remove all the names and passwords @@ -1958,6 +1983,7 @@ class Http(object): ca_certs=self.ca_certs, disable_ssl_certificate_validation=self.disable_ssl_certificate_validation, ssl_version=self.ssl_version, + key_password=certs[0][2], ) else: conn = self.connections[conn_key] = connection_type( diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py index 4312f30..06b86bf 100644 --- a/python3/httplib2/__init__.py +++ b/python3/httplib2/__init__.py @@ -175,7 +175,7 @@ DEFAULT_TLS_VERSION = getattr(ssl, "PROTOCOL_TLS", None) or getattr( def _build_ssl_context( disable_ssl_certificate_validation, ca_certs, cert_file=None, key_file=None, - maximum_version=None, minimum_version=None, + maximum_version=None, minimum_version=None, key_password=None, ): if not hasattr(ssl, "SSLContext"): raise RuntimeError("httplib2 requires Python 3.2+ for ssl.SSLContext") @@ -207,7 +207,7 @@ def _build_ssl_context( context.load_verify_locations(ca_certs) if cert_file: - context.load_cert_chain(cert_file, key_file) + context.load_cert_chain(cert_file, key_file, key_password) return context @@ -959,8 +959,13 @@ class Credentials(object): class KeyCerts(Credentials): """Identical to Credentials except that name/password are mapped to key/cert.""" + def add(self, key, cert, domain, password): + self.credentials.append((domain.lower(), key, cert, password)) - pass + def iter(self, domain): + for (cdomain, key, cert, password) in self.credentials: + if cdomain == "" or domain == cdomain: + yield (key, cert, password) class AllHosts(object): @@ -1245,6 +1250,7 @@ class HTTPSConnectionWithTimeout(http.client.HTTPSConnection): disable_ssl_certificate_validation=False, tls_maximum_version=None, tls_minimum_version=None, + key_password=None, ): self.disable_ssl_certificate_validation = disable_ssl_certificate_validation @@ -1257,15 +1263,17 @@ class HTTPSConnectionWithTimeout(http.client.HTTPSConnection): context = _build_ssl_context( self.disable_ssl_certificate_validation, self.ca_certs, cert_file, key_file, maximum_version=tls_maximum_version, minimum_version=tls_minimum_version, + key_password=key_password, ) super(HTTPSConnectionWithTimeout, self).__init__( host, port=port, - key_file=key_file, - cert_file=cert_file, timeout=timeout, context=context, ) + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password def connect(self): """Connect to a host on a given (SSL) port.""" @@ -1507,10 +1515,10 @@ class Http(object): any time a request requires authentication.""" self.credentials.add(name, password, domain) - def add_certificate(self, key, cert, domain): + def add_certificate(self, key, cert, domain, password=None): """Add a key and cert that will be used any time a request requires authentication.""" - self.certificates.add(key, cert, domain) + self.certificates.add(key, cert, domain, password) def clear_credentials(self): """Remove all the names and passwords @@ -1782,6 +1790,7 @@ a string that contains the response entity body. disable_ssl_certificate_validation=self.disable_ssl_certificate_validation, tls_maximum_version=self.tls_maximum_version, tls_minimum_version=self.tls_minimum_version, + key_password=certs[0][2], ) else: conn = self.connections[conn_key] = connection_type( diff --git a/tests/test_https.py b/tests/test_https.py index f494d7a..39d7d59 100644 --- a/tests/test_https.py +++ b/tests/test_https.py @@ -161,6 +161,30 @@ def test_client_cert_verified(): assert cert_log[0]["serialNumber"] == "E2AA6A96D1BF1AEC" +def test_client_cert_password_verified(): + cert_log = [] + + def setup_tls(context, server, skip_errors): + context.load_verify_locations(cafile=tests.CA_CERTS) + context.verify_mode = ssl.CERT_REQUIRED + return context.wrap_socket(server, server_side=True) + + def handler(request): + cert_log.append(request.client_sock.getpeercert()) + return tests.http_response_bytes() + + http = httplib2.Http(ca_certs=tests.CA_CERTS) + with tests.server_request(handler, tls=setup_tls) as uri: + uri_parsed = urllib.parse.urlparse(uri) + http.add_certificate(tests.CLIENT_ENCRYPTED_PEM, tests.CLIENT_ENCRYPTED_PEM, + uri_parsed.netloc, password="12345") + http.request(uri) + + assert len(cert_log) == 1 + # TODO extract serial from tests.CLIENT_PEM + assert cert_log[0]["serialNumber"] == "E2AA6A96D1BF1AED" + + @pytest.mark.skipif( not hasattr(tests.ssl_context(), "set_servername_callback"), reason="SSLContext.set_servername_callback is not available", -- cgit v1.2.3 From 54ee0ef8ff80a5d95a96c76bdc0187cab9e76971 Mon Sep 17 00:00:00 2001 From: Sergey Shepelev Date: Tue, 15 Oct 2019 11:35:22 +0300 Subject: feature: Http.close() to clean persistent connections and sensitive data https://github.com/httplib2/httplib2/issues/148 --- python2/httplib2/__init__.py | 14 ++++++++++++-- python3/httplib2/__init__.py | 10 ++++++++++ tests/test_other.py | 10 ++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py index d807bc1..e89e859 100644 --- a/python2/httplib2/__init__.py +++ b/python2/httplib2/__init__.py @@ -1674,6 +1674,16 @@ class Http(object): # Keep Authorization: headers on a redirect. self.forward_authorization_headers = False + def close(self): + """Close persistent connections, clear sensitive data. + Not thread-safe, requires external synchronization against concurrent requests. + """ + existing, self.connections = self.connections, {} + for _, c in existing.iteritems(): + c.close() + self.certificates.clear() + self.clear_credentials() + def __getstate__(self): state_dict = copy.copy(self.__dict__) # In case request is augmented by some foreign object such as @@ -1950,7 +1960,7 @@ class Http(object): a string that contains the response entity body. """ conn_key = '' - + try: if headers is None: headers = {} @@ -2166,7 +2176,7 @@ class Http(object): conn = self.connections.pop(conn_key, None) if conn: conn.close() - + if self.force_exception_to_status_code: if isinstance(e, HttpLib2ErrorWithResponse): response = e.response diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py index 06b86bf..6684cc4 100644 --- a/python3/httplib2/__init__.py +++ b/python3/httplib2/__init__.py @@ -1484,6 +1484,16 @@ class Http(object): # Keep Authorization: headers on a redirect. self.forward_authorization_headers = False + def close(self): + """Close persistent connections, clear sensitive data. + Not thread-safe, requires external synchronization against concurrent requests. + """ + existing, self.connections = self.connections, {} + for _, c in existing.items(): + c.close() + self.certificates.clear() + self.clear_credentials() + def __getstate__(self): state_dict = copy.copy(self.__dict__) # In case request is augmented by some foreign object such as diff --git a/tests/test_other.py b/tests/test_other.py index f87cfbd..eefd8e3 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -232,3 +232,13 @@ def test_http_443_forced_https(): assert len(m.call_args) > 0, "expected Http._request() call" conn = m.call_args[0][0] assert isinstance(conn, httplib2.HTTPConnectionWithTimeout) + + +def test_close(): + http = httplib2.Http() + assert len(http.connections) == 0 + with tests.server_const_http() as uri: + http.request(uri) + assert len(http.connections) == 1 + http.close() + assert len(http.connections) == 0 -- cgit v1.2.3 From d12344af52d57060284cca7fbe85deb6581a6942 Mon Sep 17 00:00:00 2001 From: cglouch <10370863+cglouch@users.noreply.github.com> Date: Wed, 13 Nov 2019 05:09:05 -0500 Subject: python2: regression in connect() error handling This autoformatting PR actually changed the behavior of the error handling: https://github.com/httplib2/httplib2/pull/105/files#diff-c6669c781a2dee1b2d2671cab4e21c66L985 "raise socket.err, msg" is not the same as "raise socket.error(msg)" in the case where msg is an exception. For instance consider: msg = socket.timeout("foo") raise socket.error, msg # => socket.timeout: foo raise socket.error(msg) # => socket.error: foo This has the effect of potentially changing the type of the error raised by connect(). Interestingly the PY3 copy of the code doesn't have this problem (probably cause it doesn't have msg at all; not sure if it's actually needed but I figured might as well keep it for PY2 in case it is). The change I propose here will restore the original behavior prior to the autoformat change, and will ensure that both the PY2 and PY3 copies of the code raise the same error type in the event of e.g. a socket.timeout. --- python2/httplib2/__init__.py | 20 ++++++++++++-------- tests/test_other.py | 11 +++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py index e89e859..affc057 100644 --- a/python2/httplib2/__init__.py +++ b/python2/httplib2/__init__.py @@ -1162,7 +1162,6 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection): raise ProxiesUnavailableError( "Proxy support missing but proxy use was requested!" ) - msg = "getaddrinfo returns an empty list" if self.proxy_info and self.proxy_info.isgood(): use_proxy = True proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = ( @@ -1176,7 +1175,9 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection): host = self.host port = self.port - + + socket_err = None + for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res try: @@ -1218,7 +1219,8 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection): self.sock.connect((self.host, self.port) + sa[2:]) else: self.sock.connect(sa) - except socket.error as msg: + except socket.error as e: + socket_err = e if self.debuglevel > 0: print("connect fail: (%s, %s)" % (self.host, self.port)) if use_proxy: @@ -1241,7 +1243,7 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection): continue break if not self.sock: - raise socket.error(msg) + raise socket_err or socket.error("getaddrinfo returns an empty list") class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): @@ -1338,7 +1340,6 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): def connect(self): "Connect to a host on a given (SSL) port." - msg = "getaddrinfo returns an empty list" if self.proxy_info and self.proxy_info.isgood(): use_proxy = True proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers = ( @@ -1352,7 +1353,9 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): host = self.host port = self.port - + + socket_err = None + address_info = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM) for family, socktype, proto, canonname, sockaddr in address_info: try: @@ -1435,7 +1438,8 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): raise except (socket.timeout, socket.gaierror): raise - except socket.error as msg: + except socket.error as e: + socket_err = e if self.debuglevel > 0: print("connect fail: (%s, %s)" % (self.host, self.port)) if use_proxy: @@ -1458,7 +1462,7 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): continue break if not self.sock: - raise socket.error(msg) + raise socket_err or socket.error("getaddrinfo returns an empty list") SCHEME_TO_CONNECTION = { diff --git a/tests/test_other.py b/tests/test_other.py index eefd8e3..0f450ab 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -242,3 +242,14 @@ def test_close(): assert len(http.connections) == 1 http.close() assert len(http.connections) == 0 + + +def test_connect_exception_type(): + # This autoformatting PR actually changed the behavior of error handling: + # https://github.com/httplib2/httplib2/pull/105/files#diff-c6669c781a2dee1b2d2671cab4e21c66L985 + # potentially changing the type of the error raised by connect() + # https://github.com/httplib2/httplib2/pull/150 + http = httplib2.Http() + with mock.patch("httplib2.socket.socket.connect", side_effect=socket.timeout("foo")): + with tests.assert_raises(socket.timeout): + http.request(tests.DUMMY_URL) -- cgit v1.2.3 From 095494db43abe8ddfd53b3c389baaadbfcae4f12 Mon Sep 17 00:00:00 2001 From: Sergey Shepelev Date: Thu, 19 Dec 2019 14:28:16 +0300 Subject: v0.15.0 release --- CHANGELOG | 11 +++++++++++ python2/httplib2/__init__.py | 2 +- python3/httplib2/__init__.py | 2 +- setup.py | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 10e2e4b..07fb949 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +0.15.0 + + python2: regression in connect() error handling + https://github.com/httplib2/httplib2/pull/150 + + add support for password protected certificate files + https://github.com/httplib2/httplib2/pull/143 + + feature: Http.close() to clean persistent connections and sensitive data + https://github.com/httplib2/httplib2/pull/149 + 0.14.0 Python3: PROXY_TYPE_SOCKS5 with str user/pass raised TypeError diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py index affc057..c8302eb 100644 --- a/python2/httplib2/__init__.py +++ b/python2/httplib2/__init__.py @@ -19,7 +19,7 @@ __contributors__ = [ "Alex Yu", ] __license__ = "MIT" -__version__ = '0.14.0' +__version__ = '0.15.0' import base64 import calendar diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py index 6684cc4..d8c3d34 100644 --- a/python3/httplib2/__init__.py +++ b/python3/httplib2/__init__.py @@ -15,7 +15,7 @@ __contributors__ = [ "Alex Yu", ] __license__ = "MIT" -__version__ = '0.14.0' +__version__ = '0.15.0' import base64 import calendar diff --git a/setup.py b/setup.py index db1db61..33c8827 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import setuptools.command.test import sys pkgdir = {"": "python%s" % sys.version_info[0]} -VERSION = '0.14.0' +VERSION = '0.15.0' # `python setup.py test` uses existing Python environment, no virtualenv, no pip. -- cgit v1.2.3