Ssl.SSLError: версия протокола предупреждения tlsv1

Я использую REST API для устройства Cisco CMX и пытаюсь написать код Python, который отправляет запрос GET в API для получения информации. Код выглядит так же, как и в файле, за исключением изменения необходимой информации.

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()

Тем не менее, я постоянно получаю следующую ошибку:

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)

Я также пытался обновить OpenSSL, но это не имело никакого эффекта.

Ответ 1

У меня была такая же ошибка, и Google привел меня к этому вопросу, так что вот что я сделал, надеясь, что это поможет другим в подобной ситуации.

Это применимо для OS X.

Проверьте в терминале, какую версию OpenSSL у меня:

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

Поскольку моя версия OpenSSL была слишком старой, принятый ответ не работал.

Поэтому мне пришлось обновить OpenSSL. Для этого я обновил Python до последней версии (начиная с версии 3.5 до версии 3.6) с помощью Homebrew, следуя некоторым из предложенных шагов здесь:

$ brew update
$ brew install openssl
$ brew install python3

Затем у меня возникли проблемы с PATH и версией используемого python, поэтому я только что создал новый virtualenv, убедившись, что была выбрана самая новая версия python:

$ virtualenv webapp --python=python3.6

Проблема решена.

Ответ 2

Единственное, что вам нужно сделать, это установить requests[security] в вашем virtualenv. Вы не должны использовать Python 3 (он должен работать в Python 2.7). Более того, если вы используете последнюю версию macOS, вам не нужно использовать homebrew для отдельной установки OpenSSL.

$ 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] позволяет запросам использовать последнюю версию TLS при согласовании соединения. Встроенный openssl в macOS поддерживает TLS v1.2.

Перед установкой собственной версии OpenSSL задайте этот вопрос: как загружается Google Chrome https://github.com?

Ответ 3

Ни один из принятых ответов не указал мне верное направление, и это все еще вопрос, который возникает при поиске темы, поэтому здесь моя (частично) успешная сага.

Предыстория: я запускаю скрипт Python на Beaglebone Black, который опрашивает криптовалютный обмен Poloniex, используя библиотеку python-poloniex. Он внезапно перестал работать с ошибкой TLSV1_ALERT_PROTOCOL_VERSION.

Оказывается, что с OpenSSL все было в порядке, и попытка форсировать соединение v1.2 была огромной погоней - библиотека будет использовать последнюю версию по мере необходимости. Слабым звеном в цепочке был фактически Python, который определял только ssl.PROTOCOL_TLSv1_2 и поэтому начал поддерживать TLS v1.2, начиная с версии 3.4.

Между тем, версия Debian на Beaglebone считает Python 3.3 самой последней. Обходной путь, который я использовал, состоял в том, чтобы установить Python 3.5 из исходного кода (возможно, со временем 3.4 тоже сработал, но после нескольких часов проб и ошибок я закончил):

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

Может быть, не все эти пакеты являются строго необходимыми, но их установка сразу экономит кучу повторных попыток. altinstall позволяет установке забивать существующие двоичные файлы python, устанавливая вместо этого как python3.5, хотя это означает, что вам необходимо переустановить дополнительные библиотеки. ./configure заняла добрых пять или десять минут. На make ушло пару часов.

Теперь это все еще не работает, пока я наконец не побежал

sudo -H pip3.5 install requests[security]

Который также устанавливает pyOpenSSL, cryptography и idna. Я подозреваю, что pyOpenSSL был ключевым, так что, возможно, pip3.5 install -U pyopenssl было бы достаточно, но я уже потратил слишком много времени на это, чтобы убедиться.

Итак, в итоге, если вы получаете ошибку TLSV1_ALERT_PROTOCOL_VERSION в Python, это, вероятно, потому что вы не можете поддерживать TLS v1.2. Чтобы добавить поддержку, вам нужно как минимум следующее:

  • OpenSSL 1.0.1
  • Python 3.4
  • Запросы [безопасность]

Это позволило мне преодолеть TLSV1_ALERT_PROTOCOL_VERSION, и теперь вместо этого я вступаю в битву с SSL23_GET_SERVER_HELLO.

Оказывается, это вернулось к первоначальной проблеме Python, выбрав неправильную версию SSL. Это можно подтвердить с помощью этого трюка для монтирования сеанса запросов с ssl_version=ssl.PROTOCOL_TLSv1_2. Без него используется SSLv23 и появляется ошибка SSL23_GET_SERVER_HELLO. С этим запрос успешно выполняется.

Последнее сражение состояло в том, чтобы заставить TLSv1_2 быть выбранным, когда запрос сделан глубоко в сторонней библиотеке. И этот метод, и этот метод должны были сделать свое дело, но ни один не имел никакого значения. Мое окончательное решение ужасно, но эффективно. Я отредактировал /usr/local/lib/python3.5/site-packages/urllib3/util/ssl_.py и изменил

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

в

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

и вуаля, мой сценарий может, наконец, связаться с сервером снова.

Ответ 4

Я считаю, что TLSV1_ALERT_PROTOCOL_VERSION предупреждает вас о том, что сервер не хочет разговаривать с TLS v1.0. Попробуйте указать TLS v1.2 только путем вставки в эти строки:

import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)

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

Примечание. Возможно, вам понадобятся достаточно новые версии Python (возможно, возможно, версии 2.7.9+) и, возможно, OpenSSL (у меня есть "OpenSSL 1.0.2k 26 января 2017", и выше, похоже, работает YMMV)

Ответ 5

По состоянию на июль 2018 года Pypi требует, чтобы клиенты, подключающиеся к нему, использовали TLS 1.2. Это проблема, если вы используете версию python, поставляемую с MacOS (2.7.10), потому что она поддерживает только TLS 1.0. Вы можете изменить версию ssl, которую использует python, чтобы исправить проблему, или перейти на более новую версию python. Используйте homebrew для установки новой версии python за пределами расположения библиотеки по умолчанию.

brew install [email protected]

Ответ 6

Для Mac OS X

1) Обновление до Python 3.6.5 с использованием собственного установщика приложения, загруженного с официального веб-сайта языка Python https://www.python.org/downloads/

Я обнаружил, что установщик позаботится об обновлении ссылок и символических ссылок для нового Python намного лучше, чем homebrew.

2) Установите новый сертификат, используя "./Install Certificates.command", который находится в обновленном каталоге Python 3.6.

> cd "/Applications/Python 3.6/"
> sudo "./Install Certificates.command"

Ответ 7

У меня тоже есть эта проблема. В macos вот решение:

  • Шаг 1: заварить рестолл питон. Теперь вы получили Python3.7 вместо старого Python

  • Шаг 2: создайте новую базу env на python3.7. мой путь /usr/local/Cellar/python/3.7.2/bin/python3.7

Теперь вы не будете обеспокоены этой проблемой.

Ответ 8

Именно с этой проблемой я столкнулся, когда попытался gem install bundler, и меня смутили все ответы Python (поскольку я использовал Ruby). Вот моя точная ошибка:

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)

Мое решение: я обновил Ruby до последней версии (2.6.5). Задача решена.

Ответ 9

В моем случае я запускал какой-нибудь сторонний скрипт на python script.py используя python script.py и использовался python2.7, поэтому я получал ту же ошибку. Я перезапустить скрипт, используя python3 script.py тогда все в порядке.