ssl.SSLError: tlsv1 alert protocol version
Asked Answered
P

10

45

I'm using the REST API for a Cisco CMX device, and trying to write Python code which makes a GET request to the API for information. The code is as follows and is the same as that in the file except with the necessary information changed.

from http.client import HTTPSConnection
from base64 import b64encode


# Create HTTPS connection
c = HTTPSConnection("0.0.0.0")

# encode as Base64
# decode to ascii (python3 stores as byte string, need to pass as ascii 
value for auth)
username_password = b64encode(b"admin:password").decode("ascii")
headers = {'Authorization': 'Basic {0}'.format(username_password)}

# connect and ask for resource
c.request('GET', '/api/config/v1/aaa/users', headers=headers)

# response
res = c.getresponse()

data = res.read()

However, I am continuously getting the following error:

Traceback (most recent call last):
  File "/Users/finaris/PycharmProjects/test/test/test.py", line 14, in <module>
    c.request('GET', '/api/config/v1/aaa/users', headers=headers)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/client.py", line 1106, in request
    self._send_request(method, url, body, headers)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/client.py", line 1151, in _send_request
    self.endheaders(body)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/client.py", line 1102, in endheaders
    self._send_output(message_body)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/client.py", line 934, in _send_output
    self.send(msg)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/client.py", line 877, in send
    self.connect()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/client.py", line 1260, in connect
    server_hostname=server_hostname)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/ssl.py", line 377, in wrap_socket
    _context=self)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/ssl.py", line 752, in __init__
    self.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/ssl.py", line 988, in do_handshake
    self._sslobj.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/ssl.py", line 633, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:645)

I also tried updating OpenSSL but that had no effect.

Protract answered 1/6, 2017 at 20:10 Comment(3)
See github.com/CiscoDevNet/devnet-express-dna-issues/issues/16. Looks like you are using an old version of OpenSSL in your Python. This is typically the case if you are on a Mac which shipped with a really old and outdated version of OpenSSL.Ventriloquy
I run into this issue on windows getting binary tools for wemos microcontroller. The cause was that google took me the python 2.7.3 (old) version download page I thought it was the newest... currently the 2.7.14 is the latest and it doesn't have this issue anymore, it works "automatically".Practiced
For folks, who are getting this error [SSL: TLSV1_ALERT_PROTOCOL_VERSION] for all pip installs, it is because of the recent TLS deprecation by Python.org sites. See this answer.Culmination
R
71

I had the same error and google brought me to this question, so here is what I did, hoping that it helps others in a similar situation.

This is applicable for OS X.

Check in the Terminal which version of OpenSSL I had:

$ python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
>> OpenSSL 0.9.8zh 14 Jan 2016

As my version of OpenSSL was too old, the accepted answer did not work.

So I had to update OpenSSL. To do this, I updated Python to the latest version (from version 3.5 to version 3.6) with Homebrew, following some of the steps suggested here:

$ brew update
$ brew install openssl
$ brew install python3

Then I was having problems with the PATH and the version of python being used, so I just created a new virtualenv making sure that the newest version of python was taken:

$ virtualenv webapp --python=python3.6

Issue solved.

Reformatory answered 18/10, 2017 at 11:17 Comment(6)
The accepted answer itself didn't fully cover it, but in the comments I indirectly mentioned needing to update OpenSSL. Apologies that this wasn't more clear, and thanks for adding this!Protract
If anyone is still having issues this one really helped me: https://mcmap.net/q/21584/-python-referencing-old-ssl-versionDepopulate
Please refer to my answer below. I resolved my issue by installing requests[security], without using Python3, or using brew to install openssl.Permission
upgrading to 3.6 solved it on windows 10 as well. thanks.Keating
Hi, I cannot update to python3 as the script requires python2.7 (download-deps.py from cocos2dx), any suggestions?Parochialism
That's the only way I found out, sorry. Maybe you want to open a new question explaining the problem.Reformatory
P
38

The only thing you have to do is to install requests[security] in your virtualenv. You should not have to use Python 3 (it should work in Python 2.7). Moreover, if you are using a recent version of macOS, you don't have to use homebrew to separately install OpenSSL either.

$ virtualenv --python=/usr/bin/python tempenv  # uses system python
$ . tempenv/bin/activate
$ pip install requests
$ python
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 0.9.8zh 14 Jan 2016'  # this is the built-in openssl
>>> import requests
>>> requests.get('https://api.github.com/users/octocat/orgs')
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.github.com', port=443): Max retries exceeded with url: /users/octocat/orgs (Caused by SSLError(SSLError(1, u'[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)'),))
$ pip install 'requests[security]'
$ python  # install requests[security] and try again
>>> import requests
>>> requests.get('https://api.github.com/users/octocat/orgs')
<Response [200]>

requests[security] allows requests to use the latest version of TLS when negotiating the connection. The built-in openssl on macOS supports TLS v1.2.

Before you install your own version of OpenSSL, ask this question: how is Google Chrome loading https://github.com?

Permission answered 14/3, 2018 at 16:18 Comment(4)
James, what version of MacOS do you have (sw_vers) ? Does xmlrpclib -> httplib -> ssl work for you, as in searching-pypi-by-topic with pypi = ... https: ... not http ?Efta
@Efta the OS version shouldn't matter as much as the OpenSSL version, which you can get with ssl.OPENSSL_VERSION.Permission
pip install 'requests[security]' did the job, thanks.Endocrinology
Thank you, this solved all my problems while testing locally with multiple python versions.Cognate
S
17

I believe TLSV1_ALERT_PROTOCOL_VERSION is alerting you that the server doesn't want to talk TLS v1.0 to you. Try to specify TLS v1.2 only by sticking in these lines:

import ssl
from http.client import HTTPSConnection
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)

# Create HTTPS connection
c = HTTPSConnection("0.0.0.0", context=context)

Note, you may need sufficiently new versions of Python (2.7.9+ perhaps?) and possibly OpenSSL (I have "OpenSSL 1.0.2k 26 Jan 2017" and the above seems to work, YMMV)

Saddlery answered 1/6, 2017 at 20:30 Comment(3)
You have OpenSSL 0.9.8 and TLS 1.2 works? I very much doubt this: TLS 1.2 is only supported since OpenSSL 1.0.1.Ventriloquy
TLS 1.2 didn't work with OpenSSL 0.9.8, but I changed the interpreter to one which had OpenSSL 1.0.2k and TLS 1.2 worked (and thus the call to the API succeeded).Protract
@SteffenUllrich ah yes, doing python -c 'import ssl; print(ssl.OPENSSL_VERSION)' confirms that my (homebrew installed) Python is linked with "OpenSSL 1.0.2k 26 Jan 2017". The 0.9.8zh version string I showed was from running openssl version, which must be my older system-wide installation.Saddlery
L
11

None of the accepted answers pointed me in the right direction, and this is still the question that comes up when searching the topic, so here's my (partially) successful saga.

Background: I run a Python script on a Beaglebone Black that polls the cryptocurrency exchange Poloniex using the python-poloniex library. It suddenly stopped working with the TLSV1_ALERT_PROTOCOL_VERSION error.

Turns out that OpenSSL was fine, and trying to force a v1.2 connection was a huge wild goose chase - the library will use the latest version as necessary. The weak link in the chain was actually Python, which only defined ssl.PROTOCOL_TLSv1_2, and therefore started supporting TLS v1.2, since version 3.4.

Meanwhile, the version of Debian on the Beaglebone considers Python 3.3 the latest. The workaround I used was to install Python 3.5 from source (3.4 might have eventually worked too, but after hours of trial and error I'm done):

sudo apt-get install build-essential checkinstall
sudo apt-get install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev
wget https://www.python.org/ftp/python/3.5.4/Python-3.5.4.tgz
sudo tar xzf Python-3.5.4.tgz
cd Python-3.5.4
./configure
sudo make altinstall

Maybe not all those packages are strictly necessary, but installing them all at once saves a bunch of retries. The altinstall prevents the install from clobbering existing python binaries, installing as python3.5 instead, though that does mean you have to re-install additional libraries. The ./configure took a good five or ten minutes. The make took a couple of hours.

Now this still didn't work until I finally ran

sudo -H pip3.5 install requests[security]

Which also installs pyOpenSSL, cryptography and idna. I suspect pyOpenSSL was the key, so maybe pip3.5 install -U pyopenssl would have been sufficient but I've spent far too long on this already to make sure.

So in summary, if you get TLSV1_ALERT_PROTOCOL_VERSION error in Python, it's probably because you can't support TLS v1.2. To add support, you need at least the following:

  • OpenSSL 1.0.1
  • Python 3.4
  • requests[security]

This has got me past TLSV1_ALERT_PROTOCOL_VERSION, and now I get to battle with SSL23_GET_SERVER_HELLO instead.

Turns out this is back to the original issue of Python selecting the wrong SSL version. This can be confirmed by using this trick to mount a requests session with ssl_version=ssl.PROTOCOL_TLSv1_2. Without it, SSLv23 is used and the SSL23_GET_SERVER_HELLO error appears. With it, the request succeeds.

The final battle was to force TLSv1_2 to be picked when the request is made deep within a third party library. Both this method and this method ought to have done the trick, but neither made any difference. My final solution is horrible, but effective. I edited /usr/local/lib/python3.5/site-packages/urllib3/util/ssl_.py and changed

def resolve_ssl_version(candidate):
    """
    like resolve_cert_reqs
    """
    if candidate is None:
        return PROTOCOL_SSLv23

    if isinstance(candidate, str):
        res = getattr(ssl, candidate, None)
        if res is None:
            res = getattr(ssl, 'PROTOCOL_' + candidate)
        return res

    return candidate

to

def resolve_ssl_version(candidate):
    """
    like resolve_cert_reqs
    """
    if candidate is None:
        return ssl.PROTOCOL_TLSv1_2

    if isinstance(candidate, str):
        res = getattr(ssl, candidate, None)
        if res is None:
            res = getattr(ssl, 'PROTOCOL_' + candidate)
        return res

    return candidate

and voila, my script can finally contact the server again.

Lustre answered 21/1, 2018 at 5:45 Comment(1)
Thank you @Heath! I confirm that I had the same problem running Python2.7 and trying to connect to Poloniex API, and just running pip install requests[security] solved the problem. No need to upgrade to Python3 :) Much appreciated!Olia
U
7

As of July 2018, Pypi now requires that clients connecting to it use TLS 1.2. This is an issue if you're using the version of python shipped with MacOS (2.7.10) because it only supports TLS 1.0. You can change the version of ssl that python is using to fix the problem or upgrade to a newer version of python. Use homebrew to install the new version of python outside of the default library location.

brew install python@2
Unravel answered 17/7, 2018 at 15:57 Comment(1)
You really saved my day with this answer. Can I buy you a beer? Please PM me your BTC address. Honestly, I got a Python script now running under El Capitan (I know, I shouldn't use it anymore, but in that case I have to) which brings me a huge leap forward. Thank you!!!Flanker
D
6

For Mac OS X

1) Update to Python 3.6.5 using the native app installer downloaded from the official Python language website https://www.python.org/downloads/

I've found that the installer is taking care of updating the links and symlinks for the new Python a lot better than homebrew.

2) Install a new certificate using "./Install Certificates.command" which is in the refreshed Python 3.6 directory

> cd "/Applications/Python 3.6/"
> sudo "./Install Certificates.command"
Dorser answered 21/4, 2018 at 7:51 Comment(1)
At first this did not work for me (after other methods had not worked either, so I was pretty disillusioned about the prospects of using Python 3) but part of the sudo command's initial failed results suggested that I use sudo's -H flag. Doing so, make a smooth update.Baobaobab
S
2

Another source of this problem: I found that in Debian 9, the Python httplib2 is hardcoded to insist on TLS v1.0. So any application that uses httplib2 to connect to a server that insists on better security fails with TLSV1_ALERT_PROTOCOL_VERSION.

I fixed it by changing

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

to

context = ssl.SSLContext()

in /usr/lib/python3/dist-packages/httplib2/__init__.py .

Debian 10 doesn't have this problem.

Sedum answered 1/5, 2020 at 18:55 Comment(1)
This applies to Ubuntu 18.04 LTS as well. The same fix described here works.Ascanius
G
0

I got this problem too. In macos, here is the solution:

  • Step 1: brew restall python. now you got python3.7 instead of the old python

  • Step 2: build the new env base on python3.7. my path is /usr/local/Cellar/python/3.7.2/bin/python3.7

now, you'll not being disturbed by this problem.

Giralda answered 9/1, 2019 at 8:24 Comment(0)
I
0

I encountered this exact issue when I attempted gem install bundler, and I was confused by all the Python responses (since I was using Ruby). Here was my exact error:

ERROR:  Could not find a valid gem 'bundler' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: tlsv1 alert protocol version (https://rubygems.org/latest_specs.4.8.gz)

My solution: I updated Ruby to the most recent version (2.6.5). Problem solved.

Infundibulum answered 5/10, 2019 at 5:42 Comment(1)
I think this might be a better answer for the question: https://mcmap.net/q/49239/-error-installing-bundlerHebel
R
0

I ran into this issue using Flask with the flask_mqtt extension. The solution was to add this to the Python file:

app.config['MQTT_TLS_VERSION'] = ssl.PROTOCOL_TLSv1_2 
Rotarian answered 1/1, 2022 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.