web-dev-qa-db-pt.com

ssl.SSLError: versão do protocolo de alerta tlsv1

Estou usando a API REST para um dispositivo Cisco CMX e tentando gravar código Python que faz uma solicitação GET à API para obter informações. O código é o seguinte e é o mesmo que no arquivo, exceto com as informações necessárias alteradas.

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

No entanto, estou recebendo continuamente o seguinte erro: 

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)

Eu também tentei atualizar o OpenSSL, mas isso não teve efeito.

25
finaris

Eu tive o mesmo erro e o google me trouxe a esta questão, então aqui está o que eu fiz, esperando que isso ajude os outros em uma situação similar.

Isso é aplicável para o OS X.

Verifique no terminal qual versão do OpenSSL eu tinha:

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

Como minha versão do OpenSSL era muito antiga, a resposta aceita não funcionou.

Então eu tive que atualizar o OpenSSL. Para fazer isso, atualizei o Python para a versão mais recente (da versão 3.5 para a versão 3.6) com o Homebrew, seguindo algumas das etapas sugeridas aqui :

$ brew update
$ brew install openssl
$ brew install python3

Então eu estava tendo problemas com o PATH e a versão do python sendo usada, então eu acabei de criar uma nova virtualenv certificando-me de que a versão mais nova do python foi obtida:

$ virtualenv webapp --python=python3.6

Problema resolvido.

46
J0ANMM

A única coisa que você precisa fazer é instalar requests[security] no seu virtualenv. Você não deveria ter que usar o Python 3 (ele deve funcionar no Python 2.7). Além disso, se você estiver usando uma versão recente do macOS, não precisará usar homebrew para instalar separadamente o 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] permite que solicitações usem a versão mais recente do TLS ao negociar a conexão. O openssl integrado no macOS suporta o TLS v1.2.

Antes de instalar sua própria versão do OpenSSL, faça esta pergunta: como está o carregamento do Google Chrome https://github.com ?

18
James Lim

Nenhuma das respostas aceitas me apontou na direção certa, e essa ainda é a pergunta que surge quando pesquiso o tópico, então aqui está minha saga (parcialmente) bem-sucedida.

Background: Eu corro um script Python em um Beaglebone Black que pesquisa o Exchange Poloniex usando a biblioteca python-poloniex . De repente, parou de funcionar com o erro TLSV1_ALERT_PROTOCOL_VERSION.

Acontece que o OpenSSL estava bem, e tentar forçar uma conexão v1.2 foi uma enorme perseguição - a biblioteca usará a versão mais recente conforme necessário. O elo fraco na cadeia era, na verdade, o Python, que apenas definia ssl.PROTOCOL_TLSv1_2 e, portanto, começava a suportar o TLS v1.2, desde a versão 3.4.

Enquanto isso, a versão do Debian no Beaglebone considera o Python 3.3 o mais recente. A solução que usei foi instalar o Python 3.5 a partir do código-fonte (3.4 pode ter funcionado também, mas depois de horas de tentativa e erro eu terminei):

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

Talvez nem todos esses pacotes sejam estritamente necessários, mas instalá-los todos de uma vez economiza várias tentativas. O altinstall impede que a instalação destrua binários python existentes, instalando como python3.5, embora isso signifique que você tenha que reinstalar bibliotecas adicionais. O ./configure levou uns bons cinco ou dez minutos. O make demorou algumas horas.

Agora isso ainda não funcionou até que eu finalmente corri

Sudo -H pip3.5 install requests[security]

Que também instala pyOpenSSL, cryptography e idna. Eu suspeito que pyOpenSSL era a chave, então talvez pip3.5 install -U pyopenssl teria sido suficiente, mas passei tempo demais nisso para ter certeza.

Então, em resumo, se você receber o erro TLSV1_ALERT_PROTOCOL_VERSION no Python, provavelmente é porque você não pode suportar o TLS v1.2. Para adicionar suporte, você precisa pelo menos do seguinte:

  • OpenSSL 1.0.1
  • Python 3.4
  • solicita [segurança]

Isso me fez passar TLSV1_ALERT_PROTOCOL_VERSION, e agora eu vou lutar com SSL23_GET_SERVER_HELLO.

Acontece que isso está de volta à edição original do Python, selecionando a versão SSL errada. Isso pode ser confirmado usando this trick para montar uma sessão de solicitações com ssl_version=ssl.PROTOCOL_TLSv1_2. Sem isso, SSLv23 é usado e o erro SSL23_GET_SERVER_HELLO aparece. Com isso, a solicitação é bem-sucedida.

A batalha final foi forçar o TLSv1_2 a ser escolhido quando a solicitação foi feita dentro de uma biblioteca de terceiros. Ambos este método e este método deveria ter feito o truque, mas não fez qualquer diferença. Minha solução final é horrível, mas eficaz. Eu editei /usr/local/lib/python3.5/site-packages/urllib3/util/ssl_.py e mudei

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

para

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

e voila, meu script pode finalmente entrar em contato com o servidor novamente.

8
Heath Raftery

Eu acredito que TLSV1_ALERT_PROTOCOL_VERSION está alertando que o servidor não quer falar sobre o TLS v1.0 para você. Tente especificar o TLS v1.2 apenas seguindo estas linhas:

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

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

Note, você pode precisar de versões suficientemente novas do Python (2.7.9+ talvez?) E possivelmente do OpenSSL (eu tenho "OpenSSL 1.0.2k de 26 de janeiro de 2017" e o acima parece funcionar, YMMV)

6
Josh Kupershmidt

Para Mac OS X

1) Atualize para o Python 3.6.5 usando o instalador nativo do aplicativo baixado do site oficial da linguagem Python https://www.python.org/downloads/

Descobri que o instalador está cuidando de atualizar os links e links simbólicos para o novo Python muito melhor do que o homebrew. 

2) Instale um novo certificado usando "./Install Certificates.command" que está no diretório atualizado do Python 3.6 

> cd "/Applications/Python 3.6/"
> Sudo "./Install Certificates.command"
3
Claude COULOMBE

Em julho de 2018, a Pypi agora exige que os clientes que se conectam a ela usem o TLS 1.2. Isso é um problema se você estiver usando a versão do python fornecida com o MacOS (2.7.10) porque ele suporta apenas o TLS 1.0. Você pode alterar a versão do SSL que o Python está usando para corrigir o problema ou atualizar para uma versão mais recente do Python. Use o homebrew para instalar a nova versão do python fora do local da biblioteca padrão. 

brew install [email protected]
2
BenJi

Eu também tenho esse problema. Em macos, aqui está a solução:

  • Etapa 1: preparar python restall. agora você tem python3.7 em vez do antigo python

  • Etapa 2: crie a nova base env em python3.7. meu caminho é /usr/local/Cellar/python/3.7.2/bin/python3.7

agora, você não será perturbado por esse problema.

0
Maestro Pu