Python httplib SSL23_GET_SERVER_HELLO:unknown protocol

Peter picture Peter · Sep 7, 2013 · Viewed 10.6k times · Source

Note: this code works fine on Ubuntu but not on mac and instead of changing the mac/python settings locally I'm trying to make change to the code so it'll work everywhere..

import ssl 
import httplib 
httplib.HTTPConnection(server, port, timeout)

but it throws error:

[Errno 1] _ssl.c:503: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol

now code's not using urllib.request instead using httplib

I want to change the code so it'll take SSLv3 as default protocol, something like this:

ssl.SSLContext(ssl.PROTOCOL_SSLv3)

I looked around and found few links but nothing is working!

this link is for ubuntu

link for python urllib and cURL

python bug fix, but again for urllib

Answer

mata picture mata · Sep 7, 2013

Note: The HTTPSConnection constructor allows to pass an ssl context as argument since python 2.7.9, which should be used in this case.

This answer predates that change and therefore only applies to outdated versions of python.


httplib.HTTPSConnection.connect just calls ssl.wrap_socket on the opened socket to initalize a https connection, unfortunately you can't specify any parameters in python2.7 (python3 allows passing the SSLContext).

If you want to specify the protocol version, you'd need to monkey patch one of these two:

Method 1: patch httplib.HTTPSConnection.connect:

import httplib
import socket
import ssl

def connect_patched(self):
    "Connect to a host on a given (SSL) port."

    sock = socket.create_connection((self.host, self.port),
                                    self.timeout, self.source_address)
    if self._tunnel_host:
        self.sock = sock
        self._tunnel()
    self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
                                ssl_version=ssl.PROTOCOL_SSLv3)

httplib.HTTPSConnection.connect = connect_patched

This changes the protocol version for all connections made with HTTPSConnection.

Method 2: patch ssl.wrap_socket:

import ssl

wrap_socket_orig = ssl.wrap_socket

def wrap_socket_patched(sock, keyfile=None, certfile=None,
                        server_side=False, cert_reqs=ssl.CERT_NONE,
                        ssl_version=ssl.PROTOCOL_SSLv3, ca_certs=None,
                        do_handshake_on_connect=True,
                        suppress_ragged_eofs=True, ciphers=None):
    return wrap_socket_orig(sock, keyfile, certfile, server_side,
                            cert_reqs, ssl_version, ca_certs,
                            do_handshake_on_connect,
                            suppress_ragged_eofs, ciphers)

ssl.wrap_socket = wrap_socket_patched

This changes the default protocol version for all code that uses wrap_socket, therefore also affects other libraries.

edit:

Method 3: because httplib actually accesses only wrap_socket from ssl, you could also just replace httplib.ssl with a class providing wrap_socket. Using functools.partial makes it very elegant to write this:

import httplib
import ssl
from functools import partial

class fake_ssl:
    wrap_socket = partial(ssl.wrap_socket, ssl_version=ssl.PROTOCOL_SSLv3)

httplib.ssl = fake_ssl