firebush
firebush

Reputation: 5850

How to set the SNI via http.client.HTTPSConnection

I'm using http.client.HTTPSConnection to generate an HTTPS connection to my server. I cannot use the original hostname to connect to the server since this is a test setup but I need to do a proper TLS handshake with the right hostname as SNI. How do I set the SNI for the client hello of the HTTPS connection?

Judging from ssl.SSLSocket.server_hostname, if I could access the underlying socket I should be able to set server_hostname on it to the value I want. HTTPSConnection does indeed have a sock member, but it is None after construction.

In case more source code context is helpful, I'm working on the test proxy in proxy-verifier. See proxy_http1.py#L94

Upvotes: 4

Views: 2093

Answers (1)

firebush
firebush

Reputation: 5850

Steffen Ullrich guided me to the answer. There is no direct support to pass two different host names a) to connect to and b) to send via SNI and verify the certificate against. However, http.client.HTTPSConnection calls the ssl.SSLContext.wrap_socket function off of the SSLContext passed in. This code example leverages this with a simple wrapper:

hostname_connect = '192.168.1.3'
hostname_sni = 'www.example.com'

class WrapSSLContext(ssl.SSLContext):
    def wrap_socket(self, *args, **kwargs):
        kwargs['server_hostname'] = hostname_sni
        return super().wrap_socket(*args, **kwargs)

context = WrapSSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_default_certs()
connection = http.client.HTTPSConnection(hostname_connect, context=context)

Contexts created with ssl.PROTOCOL_TLS_CLIENT have context.check_hostname set by default. And thus, context.verify_mode = ssl.CERT_REQUIRED as well.

Upvotes: 5

Related Questions