web-dev-qa-db-fra.com

ssl.SSLError: version du protocole d'alerte tlsv1

J'utilise l'API REST pour un périphérique Cisco CMX et j'essaie d'écrire du code Python qui envoie une demande GET à l'API pour obtenir des informations. Le code est le suivant et correspond à celui du fichier sauf que les informations nécessaires ont été modifiées.

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

Cependant, j'obtiens continuellement l'erreur suivante: 

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)

J'ai également essayé de mettre à jour OpenSSL mais cela n'a eu aucun effet.

25
finaris

J'ai eu la même erreur et Google m'a amené à cette question, alors voici ce que j'ai fait, en espérant que cela aide les autres dans une situation similaire.

Ceci est applicable pour OS X.

Vérifiez dans le terminal quelle version d'OpenSSL j'avais:

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

Comme ma version d'OpenSSL était trop ancienne, la réponse acceptée ne fonctionnait pas.

J'ai donc dû mettre à jour OpenSSL. Pour ce faire, j'ai mis à jour Python vers la dernière version (de la version 3.5 à la version 3.6) avec Homebrew, en suivant certaines des étapes suggérées ici :

$ brew update
$ brew install openssl
$ brew install python3

Ensuite, j'avais des problèmes avec PATH et la version de python utilisée. Je viens donc de créer une nouvelle variable virtualenv en m'assurant que la dernière version de python a bien été prise:

$ virtualenv webapp --python=python3.6

Problème résolu.

46
J0ANMM

La seule chose à faire est d’installer requests[security] dans votre virtualenv. Vous ne devriez pas avoir à utiliser Python 3 (cela devrait fonctionner dans Python 2.7). De plus, si vous utilisez une version récente de macOS, vous n'avez pas non plus besoin d'utiliser homebrew pour installer OpenSSL séparément.

$ 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] permet aux demandes d'utiliser la dernière version de TLS lors de la négociation de la connexion. Openssl intégré sur macOS prend en charge TLS v1.2.

Avant d’installer votre propre version d’OpenSSL, posez cette question: comment Google Chrome charge-t-il https://github.com ?

18
James Lim

Aucune des réponses acceptées ne m'a orienté dans la bonne direction, et c'est toujours la question qui se pose lors de la recherche du sujet, voici donc ma saga (partiellement) réussie.

Arrière-plan: J'exécute un script Python sur un Beaglebone Black qui interroge l'échange de crypto-monnaie Poloniex à l'aide de la bibliothèque python-poloniex . Il a soudainement cessé de fonctionner avec l'erreur TLSV1_ALERT_PROTOCOL_VERSION.

Il se trouve qu'OpenSSL fonctionnait bien et que tenter de forcer une connexion v1.2 était une énorme chasse à l'oie sauvage - la bibliothèque utilisera la dernière version si nécessaire. Le maillon faible de la chaîne était en fait Python, qui ne définissait que ssl.PROTOCOL_TLSv1_2 , et a donc commencé à prendre en charge TLS v1.2 à partir de la version 3.4.

Pendant ce temps, la version de Debian sur Beaglebone considère que Python 3.3 est la dernière. La solution de contournement que j'ai utilisée consistait à installer Python 3.5 à partir des sources (la version 3.4 aurait peut-être aussi fonctionné, mais après des heures d'essais et d'erreurs, j'ai terminé):

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

Tous ces paquets ne sont peut-être pas strictement nécessaires, mais les installer tous en même temps économise de nombreuses tentatives. La variable altinstall empêche l’installation de compresser les fichiers binaires python existants, en l’installant plutôt en tant que python3.5, bien que cela signifie que vous devez réinstaller des bibliothèques supplémentaires. Le ./configure a pris cinq ou dix bonnes minutes. La make a pris quelques heures.

Maintenant, cela n'a toujours pas fonctionné jusqu'à ce que j'ai finalement couru

Sudo -H pip3.5 install requests[security]

Qui installe également pyOpenSSL, cryptography et idna. Je soupçonne que pyOpenSSL était la clé, alors peut-être que pip3.5 install -U pyopenssl aurait été suffisant, mais j'ai déjà passé beaucoup trop de temps là-dessus pour m'en assurer.

En résumé, si vous obtenez une erreur TLSV1_ALERT_PROTOCOL_VERSION en Python, c'est probablement parce que vous ne pouvez pas prendre en charge TLS v1.2. Pour ajouter du support, vous avez besoin au minimum des éléments suivants:

  • OpenSSL 1.0.1
  • Python 3.4
  • demandes [sécurité]

Cela m'a permis de dépasser TLSV1_ALERT_PROTOCOL_VERSION, et je peux maintenant me battre avec SSL23_GET_SERVER_HELLO.

Il s’avère que cela revient à la question originale de Python qui sélectionnait la mauvaise version de SSL. Cela peut être confirmé en utilisant this astuce pour monter une session de requêtes avec ssl_version=ssl.PROTOCOL_TLSv1_2. Sans cela, SSLv23 est utilisé et l'erreur SSL23_GET_SERVER_HELLO apparaît. Avec cela, la demande aboutit.

La bataille finale consistait à forcer la sélection de TLSv1_2 lorsque la demande était faite au plus profond d'une bibliothèque tierce. Les deux cette méthode et cette méthode auraient dû faire l'affaire, mais ne faisaient aucune différence. Ma solution finale est horrible, mais efficace. J'ai édité /usr/local/lib/python3.5/site-packages/urllib3/util/ssl_.py et changé

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

et le tour est joué, mon script peut enfin contacter le serveur.

8
Heath Raftery

Je crois que TLSV1_ALERT_PROTOCOL_VERSION vous alerte que le serveur ne veut pas vous parler de TLS v1.0. Essayez de spécifier TLS v1.2 uniquement en respectant les lignes suivantes:

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

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

Notez que vous aurez peut-être besoin de suffisamment de nouvelles versions de Python (2.7.9+ peut-être?) Et éventuellement d'OpenSSL (j'ai "OpenSSL 1.0.2k 26 Jan 2017" et ce qui précède semble fonctionner, YMMV)

6
Josh Kupershmidt

Pour Mac OS X

1) Mise à jour vers Python 3.6.5 à l’aide du programme d’installation d’application native téléchargé à partir du site Web officiel du langage Python https://www.python.org/downloads/

J'ai constaté que l'installateur s'occupe de la mise à jour des liens et des liens symboliques pour le nouveau Python bien mieux que l'homebrew. 

2) Installez un nouveau certificat en utilisant "./Install Certificates.command" qui se trouve dans le répertoire Python 3.6 actualisé. 

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

Depuis juillet 2018, Pypi exige désormais que les clients qui s'y connectent utilisent TLS 1.2. C'est un problème si vous utilisez la version de Python fournie avec MacOS (2.7.10), car elle ne prend en charge que TLS 1.0. Vous pouvez modifier la version de ssl utilisée par python pour résoudre le problème ou passer à une version plus récente de python. Utilisez homebrew pour installer la nouvelle version de python en dehors de l’emplacement par défaut de la bibliothèque. 

brew install python@2
2
BenJi

J'ai aussi ce problème . Dans macos, voici la solution:

  • Étape 1: brassez du python. maintenant vous avez python3.7 au lieu de l'ancien python

  • Étape 2: construisez la nouvelle base env sur python3.7. mon chemin est /usr/local/Cellar/python/3.7.2/bin/python3.7

maintenant, vous ne serez pas dérangé par ce problème.

0
Maestro Pu