Beispiel #1
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = DebugSslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.set_cipher_list('ECDH')  # Needed for test_get_ecdh_param()
        ssl_client.do_handshake()
        self.ssl_client = ssl_client
Beispiel #2
0
    def test_ignore_client_authentication_requests(self):

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(10)
        sock.connect(("auth.startssl.com", 443))

        ssl_client = DebugSslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE,
                                    ignore_client_authentication_requests=True)

        ssl_client.do_handshake()
        self.assertGreater(ssl_client.get_client_CA_list(), 2)
Beispiel #3
0
    def test_ignore_client_authentication_requests(self):

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(10)
        sock.connect(("auth.startssl.com", 443))

        ssl_client = DebugSslClient(ssl_version=SSLV23,
                                    sock=sock,
                                    ssl_verify=SSL_VERIFY_NONE,
                                    ignore_client_authentication_requests=True)

        ssl_client.do_handshake()
        self.assertGreater(ssl_client.get_client_CA_list(), 2)
Beispiel #4
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = DebugSslClient(ssl_version=SSLV23,
                                    sock=sock,
                                    ssl_verify=SSL_VERIFY_NONE)
        self.ssl_client = ssl_client
Beispiel #5
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('www.google.com', 443))

        ssl_client = DebugSslClient(ssl_version=OpenSslVersionEnum.SSLV23,
                                    sock=sock,
                                    ssl_verify=OpenSslVerifyEnum.NONE)
        self.ssl_client = ssl_client
Beispiel #6
0
    def test_client_certificate_requested(self):

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(10)
        sock.connect(("auth.startssl.com", 443))

        ssl_client = DebugSslClient(ssl_version=SSLV23,
                                    sock=sock,
                                    ssl_verify=SSL_VERIFY_NONE)

        self.assertRaisesRegexp(ClientCertificateRequested,
                                'Server requested a client certificate',
                                ssl_client.do_handshake)
Beispiel #7
0
    def __init__(self,
                 hostname,                              # type: Text
                 ip_address,                            # type: Text
                 port,                                  # type: int
                 ssl_version,                           # type: OpenSslVersionEnum
                 ssl_verify_locations=None,             # type: Optional[Text]
                 client_auth_creds=None,                # type: Optional[ClientAuthenticationCredentials]
                 should_ignore_client_auth=False        # type: bool
                 ):
        # type: (...) -> None
        if client_auth_creds:
            # A client certificate and private key were provided
            self.ssl_client = DebugSslClient(ssl_version=ssl_version,
                                             ssl_verify=OpenSslVerifyEnum.NONE,
                                             ssl_verify_locations=ssl_verify_locations,
                                             client_certchain_file=client_auth_creds.client_certificate_chain_path,
                                             client_key_file=client_auth_creds.client_key_path,
                                             client_key_type=client_auth_creds.client_key_type,
                                             client_key_password=client_auth_creds.client_key_password,
                                             ignore_client_authentication_requests=False)
        else:
            # No client cert and key
            self.ssl_client = DebugSslClient(ssl_version=ssl_version,
                                             ssl_verify=OpenSslVerifyEnum.NONE,
                                             ssl_verify_locations=ssl_verify_locations,
                                             ignore_client_authentication_requests=should_ignore_client_auth)

        self.ssl_client.set_cipher_list(self.DEFAULT_SSL_CIPHER_LIST)

        self._hostname = hostname
        self._ip_address = ip_address
        self._port = port

        # Can be set later
        self._tunnel_host = None
        self._tunnel_port = None
        self._tunnel_basic_auth_token = None
Beispiel #8
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = DebugSslClient(ssl_version=SSLV23,
                                    sock=sock,
                                    ssl_verify=SSL_VERIFY_NONE)
        ssl_client.set_cipher_list('ECDH')  # Needed for test_get_ecdh_param()
        ssl_client.do_handshake()
        self.ssl_client = ssl_client
Beispiel #9
0
class SslClient_Tests_PrivateKey(unittest.TestCase):

    def setUp(self):
        self.ssl_client = DebugSslClient(ssl_version=SSLV23, ssl_verify=SSL_VERIFY_NONE)

        test_file = tempfile.NamedTemporaryFile(delete=False)
        test_file.write("""-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,7D15D836EE9E1B77

fzTe/7+BUBBpW7rFqfffSMeNTNwjVT8uT6+aQFkv1sazU295heEWcvnqYPQ2suDS
dqud4pxLizkSRZpAIoKZV/E0z3iM1zsrGiyNXZ3mouRjSZdESEBnPEbtIdsyHLkL
9arhA/kvuMqXMjgun+tPD0+ETlaFf5GCKgfFQzbF2W4WpeEXii43ZLZ9UmObUUql
5Y65K/07+ga/dj3E+l1dLtA7VhVV5CK+8TTmVdqOr85pEZ/BC3U09vnwovDWJ+l0
sV7GhzsDFSpwxeArZy7wSMkSOTe71O1gvjOxWlupznFcZvirhRtI+5k1/btcn7hx
8b7dp36pTb/GfwaeUVsAvJBqwdSun3NOWX7zJxIDGU6LxA80eiV4z3SxAykS52gl
rlb2e+F6dV+tRuREfaDaeS1DSlDMp1mQjPSD2ix6nSypv19FHdh01OoCd0OFxM6D
xs5RQnUeu4J9g45Wdp6lmXM62EhUqYLKRbjXnZbFMlVMq81UwpMazwAruTEOCxl4
iQk3rNzfREONa9HeshiMlkeRAQpyB1qLZwhoTwTl6xKaMkt6nFEE6qX1KrrACHkH
CFJVbuWVJCyoRFv+0Gypi7zn1ZZGkE4inDHxqIzUa0sSmbShEWooTxCyGUSoosaY
u2ozh8ESQCy03JFR9DY6mo3YekbIcCEjgdmE35nK4lJQFbo3A8YlHunEdVK0tb8Z
Wxf7cJ6J55bG5/Kft65kJnXAHrV9LnM1tPiRkB8umZkj/ou5NpDKiuLjR+WBfwi0
tqXk90NdSqJtMMGgrtVM84TYFPXP58QCBnE9oAI7XYM1rusuVBOXZw==
-----END RSA PRIVATE KEY-----
""")
        test_file.close()
        self.test_file = test_file
        test_file2 = tempfile.NamedTemporaryFile(delete=False)
        test_file2.write("""-----BEGIN CERTIFICATE-----
MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx
DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy
ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG
9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1
OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx
IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy
MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw
ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO
8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo
w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O
zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D
NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY
gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm
dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk
Pd2eQ9+DkopOz3UGU7c=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx
DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy
ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG
9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1
OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx
IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy
MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw
ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO
8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo
w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O
zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D
NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY
gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm
dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk
Pd2eQ9+DkopOz3UGU7c=
-----END CERTIFICATE-----
""")
        test_file2.close()
        self.testFile2 = test_file2


    def test_use_private_key(self):
        self.assertIsNone(self.ssl_client._use_private_key(self.testFile2.name, self.test_file.name, SSL_FILETYPE_PEM,
                                                         'testPW'))


    def test_use_private_key_bad(self):
        with self.assertRaises(ValueError):
            self.ssl_client._use_private_key(self.testFile2.name, self.test_file.name, SSL_FILETYPE_PEM, 'badPW')
Beispiel #10
0
#!/usr/bin/python2.7
from __future__ import print_function
import os
import sys
sys.path.insert(1, os.path.join(os.path.dirname(__file__), u'lib'))

from nassl.ssl_client import OpenSslVersionEnum
import socket
from nassl.debug_ssl_client import DebugSslClient

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect((u'www.yahoo.com', 443))

ssl_client = DebugSslClient(ssl_version=OpenSslVersionEnum.TLSV1_2,
                            sock=sock,
                            ssl_verify_locations=u'mozilla.pem')
ssl_client.set_tlsext_status_ocsp()
ssl_client.do_handshake()

print(u'Certificate chain')
for cert in ssl_client.get_peer_cert_chain():
    print(cert.as_dict()[u'subject'][u'commonName'])

ocsp_resp = ssl_client.get_tlsext_status_ocsp_resp()
print(ocsp_resp.verify(u'mozilla.pem'))

print(u'\nCipher suite')
print(ssl_client.get_current_cipher_name())

print(u'\nHTTP response')
Beispiel #11
0
#!/usr/bin/python2.7
import socket
from nassl import TLSV1, SSL_VERIFY_NONE
from nassl.debug_ssl_client import DebugSslClient


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect(("www.google.com", 443))

ssl_client = DebugSslClient(ssl_version=TLSV1, sock=sock, ssl_verify=SSL_VERIFY_NONE)
ssl_client.do_handshake()

print 'Certificate chain'
for cert in ssl_client.get_peer_cert_chain():
    print cert.as_dict()['subject']['commonName']

print '\nCipher suite'
print ssl_client.get_current_cipher_name()

print '\nHTTP response'
ssl_client.write('GET / HTTP/1.0\r\nUser-Agent: Test\r\nHost: www.google.com\r\n\r\n')
print ssl_client.read(2048)
Beispiel #12
0
class SslClient_Tests_PrivateKey(unittest.TestCase):
    def setUp(self):
        self.ssl_client = DebugSslClient(ssl_version=SSLV23,
                                         ssl_verify=SSL_VERIFY_NONE)

        test_file = tempfile.NamedTemporaryFile(delete=False)
        test_file.write("""-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,7D15D836EE9E1B77

fzTe/7+BUBBpW7rFqfffSMeNTNwjVT8uT6+aQFkv1sazU295heEWcvnqYPQ2suDS
dqud4pxLizkSRZpAIoKZV/E0z3iM1zsrGiyNXZ3mouRjSZdESEBnPEbtIdsyHLkL
9arhA/kvuMqXMjgun+tPD0+ETlaFf5GCKgfFQzbF2W4WpeEXii43ZLZ9UmObUUql
5Y65K/07+ga/dj3E+l1dLtA7VhVV5CK+8TTmVdqOr85pEZ/BC3U09vnwovDWJ+l0
sV7GhzsDFSpwxeArZy7wSMkSOTe71O1gvjOxWlupznFcZvirhRtI+5k1/btcn7hx
8b7dp36pTb/GfwaeUVsAvJBqwdSun3NOWX7zJxIDGU6LxA80eiV4z3SxAykS52gl
rlb2e+F6dV+tRuREfaDaeS1DSlDMp1mQjPSD2ix6nSypv19FHdh01OoCd0OFxM6D
xs5RQnUeu4J9g45Wdp6lmXM62EhUqYLKRbjXnZbFMlVMq81UwpMazwAruTEOCxl4
iQk3rNzfREONa9HeshiMlkeRAQpyB1qLZwhoTwTl6xKaMkt6nFEE6qX1KrrACHkH
CFJVbuWVJCyoRFv+0Gypi7zn1ZZGkE4inDHxqIzUa0sSmbShEWooTxCyGUSoosaY
u2ozh8ESQCy03JFR9DY6mo3YekbIcCEjgdmE35nK4lJQFbo3A8YlHunEdVK0tb8Z
Wxf7cJ6J55bG5/Kft65kJnXAHrV9LnM1tPiRkB8umZkj/ou5NpDKiuLjR+WBfwi0
tqXk90NdSqJtMMGgrtVM84TYFPXP58QCBnE9oAI7XYM1rusuVBOXZw==
-----END RSA PRIVATE KEY-----
""")
        test_file.close()
        self.test_file = test_file
        test_file2 = tempfile.NamedTemporaryFile(delete=False)
        test_file2.write("""-----BEGIN CERTIFICATE-----
MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx
DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy
ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG
9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1
OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx
IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy
MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw
ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO
8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo
w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O
zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D
NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY
gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm
dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk
Pd2eQ9+DkopOz3UGU7c=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx
DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy
ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG
9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1
OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx
IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy
MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw
ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO
8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo
w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O
zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D
NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY
gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm
dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk
Pd2eQ9+DkopOz3UGU7c=
-----END CERTIFICATE-----
""")
        test_file2.close()
        self.testFile2 = test_file2

    def test_use_private_key(self):
        self.assertIsNone(
            self.ssl_client._use_private_key(self.testFile2.name,
                                             self.test_file.name,
                                             SSL_FILETYPE_PEM, 'testPW'))

    def test_use_private_key_bad(self):
        with self.assertRaises(ValueError):
            self.ssl_client._use_private_key(self.testFile2.name,
                                             self.test_file.name,
                                             SSL_FILETYPE_PEM, 'badPW')
Beispiel #13
0
class SSLConnection(object):
    """Base SSL connection class which leverages an nassl.SslClient for performing the SSL handshake.
    """

    # The following errors mean that the server explicitly rejected the handshake. The goal to differentiate rejected
    # handshakes from random network errors such as the server going offline, etc.
    HANDSHAKE_REJECTED_SOCKET_ERRORS = {'was forcibly closed': 'Received FIN',
                                        'reset by peer': 'Received RST'}

    HANDSHAKE_REJECTED_SSL_ERRORS = {'sslv3 alert handshake failure': 'Alert handshake failure',
                                     'no ciphers available': 'No ciphers available',
                                     'excessive message size': 'Excessive message size',
                                     'bad mac decode': 'Bad mac decode',
                                     'wrong version number': 'Wrong version number',
                                     'no cipher match': 'No cipher match',
                                     'bad decompression': 'Bad decompression',
                                     'peer error no cipher': 'Peer error no cipher',
                                     'no cipher list': 'No ciphers list',
                                     'insufficient security': 'Insufficient security',
                                     'block type is not 01': 'block type is not 01',  # Actually an RSA error
                                     'tlsv1 alert protocol version': 'Alert: protocol version '}

    # Constants for tunneling the traffic through a proxy
    HTTP_CONNECT_REQ = 'CONNECT {0}:{1} HTTP/1.1\r\n\r\n'
    HTTP_CONNECT_REQ_PROXY_AUTH_BASIC = 'CONNECT {0}:{1} HTTP/1.1\r\nProxy-Authorization: Basic {2}\r\n\r\n'

    # Errors caused by the proxy
    ERR_CONNECT_REJECTED = 'The proxy rejected the CONNECT request for this host'
    ERR_PROXY_OFFLINE = 'Could not connect to the proxy: "{0}"'

    # Restrict cipher list to make the client hello smaller so we don't run into
    # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665452
    DEFAULT_SSL_CIPHER_LIST = 'HIGH:MEDIUM:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA'

    # Default socket settings global to all SSLyze connections; can be overridden
    NETWORK_MAX_RETRIES = 3
    NETWORK_TIMEOUT = 5

    @classmethod
    def set_global_network_settings(cls, network_max_retries, network_timeout):
        # Not thread-safe
        cls.NETWORK_MAX_RETRIES = network_max_retries
        cls.NETWORK_TIMEOUT = network_timeout

    def __init__(self,
                 hostname,                              # type: Text
                 ip_address,                            # type: Text
                 port,                                  # type: int
                 ssl_version,                           # type: OpenSslVersionEnum
                 ssl_verify_locations=None,             # type: Optional[Text]
                 client_auth_creds=None,                # type: Optional[ClientAuthenticationCredentials]
                 should_ignore_client_auth=False        # type: bool
                 ):
        # type: (...) -> None
        if client_auth_creds:
            # A client certificate and private key were provided
            self.ssl_client = DebugSslClient(ssl_version=ssl_version,
                                             ssl_verify=OpenSslVerifyEnum.NONE,
                                             ssl_verify_locations=ssl_verify_locations,
                                             client_certchain_file=client_auth_creds.client_certificate_chain_path,
                                             client_key_file=client_auth_creds.client_key_path,
                                             client_key_type=client_auth_creds.client_key_type,
                                             client_key_password=client_auth_creds.client_key_password,
                                             ignore_client_authentication_requests=False)
        else:
            # No client cert and key
            self.ssl_client = DebugSslClient(ssl_version=ssl_version,
                                             ssl_verify=OpenSslVerifyEnum.NONE,
                                             ssl_verify_locations=ssl_verify_locations,
                                             ignore_client_authentication_requests=should_ignore_client_auth)

        self.ssl_client.set_cipher_list(self.DEFAULT_SSL_CIPHER_LIST)

        self._hostname = hostname
        self._ip_address = ip_address
        self._port = port

        # Can be set later
        self._tunnel_host = None
        self._tunnel_port = None
        self._tunnel_basic_auth_token = None

    def enable_http_connect_tunneling(self, tunnel_host, tunnel_port, tunnel_user=None, tunnel_password=None):
        # type: (Text, int, Optional[Text], Optional[Text]) -> None
        """Proxy the traffic through an HTTP Connect proxy.
        """
        self._tunnel_host = tunnel_host
        self._tunnel_port = tunnel_port
        self._tunnel_basic_auth_token = None
        if tunnel_user is not None:
            self._tunnel_basic_auth_token = b64encode(
                '{0}:{1}'.format(quote(tunnel_user), quote(tunnel_password)).encode('utf-8')
            )

    def write(self, data):
        # type: (bytes) -> int
        return self.ssl_client.write(data)

    def read(self, size):
        # type: (int) -> bytes
        return self.ssl_client.read(size)

    def do_pre_handshake(self, network_timeout):
        # type: (int) -> socket
        """Open a socket to the server; setup HTTP tunneling if a proxy was configured.
        """
        if self._tunnel_host:
            # Proxy configured; setup HTTP tunneling
            try:
                sock = socket.create_connection((self._tunnel_host, self._tunnel_port), network_timeout)
            except socket.timeout as e:
                raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e)))
            except socket.error as e:
                raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e)))

            # Send a CONNECT request with the host we want to tunnel to
            if self._tunnel_basic_auth_token is None:
                sock.send(self.HTTP_CONNECT_REQ.format(self._hostname, self._port).encode('utf-8'))
            else:
                sock.send(self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format(self._hostname,
                                                                        self._port,
                                                                        self._tunnel_basic_auth_token).encode('utf-8'))
            http_response = HttpResponseParser.parse(sock)

            # Check if the proxy was able to connect to the host
            if http_response.status != 200:
                raise ProxyError(self.ERR_CONNECT_REJECTED)
        else:
            # No proxy; connect directly to the server
            sock = socket.create_connection(address=(self._ip_address, self._port), timeout=network_timeout)

        # Pass the connected socket to the SSL client
        self.ssl_client.set_underlying_socket(sock)
        return sock

    def connect(self, network_timeout=None, network_max_retries=None):
        # type: (int, int) -> None
        final_timeout = self.NETWORK_TIMEOUT if network_timeout is None else network_timeout
        final_max_retries = self.NETWORK_MAX_RETRIES if network_max_retries is None else network_max_retries
        retry_attempts = 0
        delay = 0
        while True:
            try:
                # Sleep if it's a retry attempt
                time.sleep(delay)

                # StartTLS negotiation or proxy setup if needed
                self.do_pre_handshake(final_timeout)

                try:
                    # SSL handshake
                    self.ssl_client.do_handshake()

                except ClientCertificateRequested:
                    # Server expected a client certificate and we didn't provide one
                    raise
                except socket.timeout:
                    # Network timeout, propagate the error to trigger a retry
                    raise
                except (socket.error, IOError) as e:
                    # On Python 3.3+ socket.error == IOError but on Python 2.7 they are different
                    # We use the same except block so it works on all versions of Python
                    # This section is meant to handle IOErrors
                    if 'Nassl SSL handshake failed' in str(e.args):
                        raise SSLHandshakeRejected('TLS / Unexpected EOF')

                    # This section is meant to handle socket.errors
                    for error_msg in self.HANDSHAKE_REJECTED_SOCKET_ERRORS.keys():
                        if error_msg in str(e.args):
                            raise SSLHandshakeRejected('TCP / ' + self.HANDSHAKE_REJECTED_SOCKET_ERRORS[error_msg])

                    # Unknown socket error
                    raise
                except _nassl.OpenSSLError as e:
                    for error_msg in self.HANDSHAKE_REJECTED_SSL_ERRORS.keys():
                        if error_msg in str(e.args):
                            raise SSLHandshakeRejected('TLS / ' + self.HANDSHAKE_REJECTED_SSL_ERRORS[error_msg])
                    raise  # Unknown SSL error if we get there

            # Pass on exceptions for rejected handshakes
            except SSLHandshakeRejected:
                raise
            except ClientCertificateRequested:
                raise
            except _nassl.OpenSSLError:
                # Raise unknown OpenSSL errors
                raise
            except socket.timeout:
                # Attempt to retry connection if a network error occurred during connection or the handshake
                retry_attempts += 1
                if retry_attempts >= final_max_retries:
                    # Exhausted the number of retry attempts, give up
                    raise
                elif retry_attempts == 1:
                    delay = random.random()
                else:
                    # Exponential back off
                    delay = min(6, 2 * delay)  # Cap max delay at 6 seconds

            else:
                # No network error occurred
                break

    def close(self):
        # type: () -> None
        self.ssl_client.shutdown()
        sock = self.ssl_client.get_underlying_socket()
        if sock:
            sock.close()

    def post_handshake_check(self):
        # type: () -> Text
        return ''