Cisco CMXデバイス にREST APIを使用しており、APIに情報のGET要求を行うPythonコードを記述しようとしています。コードは次のとおりで、必要な情報が変更されていることを除いて、ファイル内のコードと同じです。
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の更新も試みましたが、効果はありませんでした。
私は同じエラーを抱えており、グーグルはこの質問に私を連れて来たので、ここで私がやったことは、それが同様の状況の他の人を助けることを望んでいます。
これはOS Xに適用されます。
使用しているOpenSSLのバージョンをターミナルで確認します。
$ python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
>> OpenSSL 0.9.8zh 14 Jan 2016
OpenSSLのバージョンが古すぎるため、受け入れられた回答は機能しませんでした。
そのため、OpenSSLを更新する必要がありました。これを行うには、Homebrewを使用してPythonを最新バージョン(バージョン3.5からバージョン3.6)に更新し、 here の手順をいくつか提案しました。
$ brew update
$ brew install openssl
$ brew install python3
それからPATHと使用されているpythonのバージョンに問題があったので、pythonの最新バージョンが取得されたことを確認して新しいvirtualenv
を作成しました。
$ virtualenv webapp --python=python3.6
問題は解決しました。
しなければならないことは、virtualenvにrequests[security]
をインストールすることだけです。 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を使用できるようにします。 macOSの組み込みopensslはTLS v1.2をサポートします。
独自のバージョンのOpenSSLをインストールする前に、次の質問をしてください。GoogleChrome loading https://github.com ?
受け入れられた答えのどれも私を正しい方向に向けてくれませんでした。これはトピックを検索するときに出てくる質問ですので、ここに私の(部分的に)成功したサガがあります。
背景: python-poloniex ライブラリを使用して、暗号通貨交換PoloniexをポーリングするBeaglebone BlackでPythonスクリプトを実行します。 TLSV1_ALERT_PROTOCOL_VERSIONエラーで突然動作を停止しました。
OpenSSLは問題なく、v1.2接続を強制しようとするのは非常に大げさな追跡でした。ライブラリは必要に応じて最新バージョンを使用します。チェーン内の弱いリンクは実際にはPythonであり、 ssl.PROTOCOL_TLSv1_2
のみを定義したため、バージョン3.4以降、TLS v1.2のサポートを開始しました。
一方、Beaglebone上のDebianのバージョンでは、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
には5〜10分かかりました。 make
は数時間かかりました。
今、私は最終的に走るまで、これはまだ動作しませんでした
Sudo -H pip3.5 install requests[security]
pyOpenSSL
、cryptography
、およびidna
もインストールされます。 pyOpenSSL
がキーだったのではないかと思うので、おそらくpip3.5 install -U pyopenssl
で十分だったかもしれませんが、確認するためにこれにかなり長い時間を費やしました。
要約すると、PythonでTLSV1_ALERT_PROTOCOL_VERSIONエラーが発生した場合、おそらくTLS v1.2をサポートできないためです。サポートを追加するには、少なくとも次のものが必要です。
これにより、TLSV1_ALERT_PROTOCOL_VERSIONを過ぎてしまい、代わりにSSL23_GET_SERVER_HELLOとの戦いになりました。
これは、Pythonが間違ったSSLバージョンを選択するという元の問題に戻っていることがわかります。これは、 this トリックを使用して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
そして出来上がり、私のスクリプトは最終的に再びサーバーに接続できます。
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 Jan 2017"が必要で、上記は動作しているようです、YMMV)
Mac OS Xの場合
1)公式Python言語Webサイトからダウンロードしたネイティブアプリインストーラーを使用してPython 3.6.5に更新 https://www.python.org/downloads/ =
インストーラーは、homebrewよりもはるかに優れた新しいPythonのリンクとシンボリックリンクの更新を処理していることがわかりました。
2)更新されたPython 3.6ディレクトリにある「./Install Certificates.command」を使用して新しい証明書をインストールします
> cd "/Applications/Python 3.6/"
> Sudo "./Install Certificates.command"
2018年7月の時点で、Pypiに接続するクライアントはTLS 1.2を使用する必要があります。 MacOS(2.7.10)に付属のpythonのバージョンを使用している場合、TLS 1.0のみをサポートするため、これは問題です。 pythonが使用しているsslのバージョンを変更して、問題を修正するか、Pythonの新しいバージョンにアップグレードできます。 homebrewを使用して、デフォルトのライブラリの場所以外にpythonの新しいバージョンをインストールします。
brew install python@2
私もこの問題を抱えています。 macosでの解決策は次のとおりです。
ステップ1:Restall Pythonを作成します。今、あなたは古いPythonの代わりにpython3.7を得ました
ステップ2:python3.7で新しいenvベースをビルドします。私のパスは/usr/local/Cellar/python/3.7.2/bin/python3.7
です
これで、この問題に悩まされることはなくなります。