SSLV3_ALERT_HANDSHAKE_FAILURE with SNI using Tornado 4.2 in Python 2.9.10

2k views Asked by At

I have an issue setting the SNI flag correctly using ssl.SSLContext in Python 2.7.10, the handshake fails every time and I can't figure out why.

Here is how I tried to do it:

import ssl
import socket

if ssl.HAS_SNI:
    print "SNI is available"
print(ssl.OPENSSL_VERSION)

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.load_cert_chain('cacrt.pem', 'cakey.pem', 'password')

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock = context.wrap_socket(sock, server_hostname='virtServer')
sock.connect(('ip.to.the.server', 443))

http_client = tornado.httpclient.HTTPClient()
http_client.fetch('https://ip.to.the.server/some_url', method='GET',  ssl_options=context)

And here is the output I get:

SNI is available
OpenSSL 0.9.8zd 8 Jan 2015
Traceback (most recent call last):
  File "test/steps/test.py", line 37, in <module>
    sock.connect(('10.59.71.242', 443))
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ssl.py", line 844, in connect
    self._real_connect(addr, False)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ssl.py", line 835, in _real_connect
    self.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ssl.py", line 808, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:590)

What am I doing wrong that is causing this error? How can I change my code to solve this issue ?

Any ideas/help on this would be much appreciated.

Thanks, Regards, Yaro

2

There are 2 answers

0
Steffen Ullrich On BEST ANSWER

It is very hard to tell without having ore information about the server, but I'll try:

OpenSSL 0.9.8zd 8 Jan 2015

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

You are restricting the protocol to TLS 1.0. It might be that the server expects TLS 1.2 or TLS 1.1 or SSL 3.0. Note that TLS 1.1+ are not support with the version of OpenSSL you are using. It might also be that the server has no shared ciphers.

Try to connect with a browser and see which TLS version is spoken and which ciphers are used. If this works but requires TLS 1.1+ try to create a connection with curl or wget. If this is a public URL use SSLLabs to check the supported protocols and ciphers.

http_client.fetch('https://ip.to.the.server/some_url', method='GET',  ssl_options=context)

If you really use an IP address here it might also be a problem that the server does not accept the IP address as the SNI name. Some servers cause a handshake failure if the given name does not match the configuration while others simply return a default certificate. Apart from that the hostname in the URL must match the name in the certificate so that the certificate validation can succeed.

0
Yaro On

Thanks Steffen, your suggestions eventually have brought me to the solution. The issue was not with the TLS version or OpenSSL, but with the fact that I was not specifying ciphers and the necessary cipher (RC4-SHA) was not in the default list in Python 2.7.10.

context.set_ciphers('RC4-SHA')

Also I could not use the created context with Tornado since I needed to provide a specific server_hostname which form what I've seen can only be done on a socket object:

sock = context.wrap_socket(sock, server_hostname='virtServer')

Instead I used sock.write('...') and sock.recv() to make the request and everything worked great.