web-dev-qa-db-ja.com

証明書の検証に失敗しました:ローカル発行者の証明書を取得できません

Pythonを使用してWebからデータを取得しようとしています。 urllib.requestパッケージをインポートしましたが、実行中にエラーが発生します。

certificate verify failed: unable to get local issuer certificate (_ssl.c:1045)

URLを「http」に変更すると、データを取得できます。しかし、これによりSSL証明書のチェックが回避されると思います。

それで、私はインターネットでチェックして、1つの解決策を見つけました:/Applications/Python\ 3.7/Install\ Certificates.commandを実行します

これで私の問題が解決しました。しかし、私はSSLなどについて知識がありません。私の問題を解決するために実際にそれが何をしたかを理解してもらえますか.

可能であれば、セキュリティと証明書について学ぶための適切なリソースをお勧めします。これは初めてです。

ありがとう!

注:リンクを確認しました- openssl、python要求エラー:「証明書の検証に失敗しました」

私の質問はリンクの質問とは異なります。なぜなら、certifiパッケージをインストールしたとき、またはInstall\ Certificates.commandを実行してエラーを修正したときに実際に何が起こるかを知りたいからです。証券の理解が不十分です。

20
Biswajit Paul

私はOSXで同じ問題にぶつかりましたが、Linuxでは私のコードはまったく問題ありませんでした。

/Applications/Python 3.7/Install Certificates.commandをポイントしたファイルを調べたところ、このコマンドがデフォルトのPythonインストールのルート証明書をcertifiパッケージを介して出荷されたものに置き換えることがわかりました。

certifiはルート証明書のセットです。各SSL証明書は信頼のチェーンに依存します。親を信頼するなど、その証明書の親を信頼するため、特定の証明書を信頼します。ある時点で、「親」はなく、それらは「ルート」証明書です。これらの場合、一般に信頼されているルート証明書(通常、「DigiCert」などの大規模な信頼会社)をバンドルする以外に解決策はありません。

たとえば、ブラウザのセキュリティ設定でルート証明書を確認できます(たとえば、Firefox-> Preference-> Privacy and security-> view certificate-> Authorities)。

最初の問題に戻って、.commandファイルを実行する前に、これを実行すると、クリーンインストールで空のリストが返されます。

import os
import ssl                                        
openssl_dir, openssl_cafile = os.path.split(      
    ssl.get_default_verify_paths().openssl_cafile)
# no content in this folder
os.listdir(openssl_dir)
# non existent file
print(os.path.exists(openssl_cafile))

これは、OSX上のPythonインストールにデフォルトの認証局がないことを意味します。可能なデフォルトは、certifiパッケージで提供されるものです。

その後、次のように適切なデフォルトを持つSSLコンテキストを作成できます(certifi.where()は認証局の場所を示します)。

import platform
# ...

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = True
ssl_context.load_default_certs()

if platform.system().lower() == 'darwin':
    import certifi
    ssl_context.load_verify_locations(
        cafile=os.path.relpath(certifi.where()),
        capath=None,
        cadata=None)

次のようにpythonからurlにリクエストします:

import urllib
# previous context
https_handler = urllib.request.HTTPSHandler(context=ssl_context)

opener = urllib.request.build_opener(https_handler)
ret = opener.open(url, timeout=2)
4
Raffi

Linuxのcondaでエラーが発生しました。私の解決策は簡単でした。

conda install -c conda-forge certifi

デフォルトの証明書には問題があるように見えるので、conda forgeを使用する必要がありました。

0
netskink