本文へスキップ
ecdsa.com
Python ssl module (OpenSSL)Python の cryptography

エラーメッセージ

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

別の表記

  • ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

Python は C ソースの位置(例:「(_ssl.c:1082)」)を付け加えます。この番号は Python のバージョンによって変わり、診断には意味がありません。urllib はこのエラーを「<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] …>」として、requests と urllib3 は「Max retries exceeded … (Caused by SSLError(…))」としてラップします。

Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)

このエラーの意味

Python の ssl モジュールが OpenSSL にサーバー証明書の検証を依頼しましたが、OpenSSL は、この SSLContext が読み込んだ CA 証明書の中に、チェーンの最後の証明書の発行者を見つけられませんでした。サーバーの設定が間違っているとは限りません。多くの場合、この Python がブラウザーや OS とは別の(または空の)CA バンドルを参照しているだけです。

発生する理由

修正方法

  1. 1.

    macOS の python.org ビルド:Install Certificates.command を実行する

    このスクリプトは certifi をインストールし、その Python の OpenSSL のバンドルとしてリンクします。別の Python のバージョンをインストールした後は、もう一度実行しなければなりません。

    bash
    # macOS, python.org installer: run once per installed Python version
    "/Applications/Python 3.14/Install Certificates.command"
  2. 2.

    truststore で OS のトラストストアを使う

    truststore を使うと、ssl.SSLContext が OS(macOS ではキーチェーン、Windows では証明書ストア、Linux ではシステムのバンドル)を通じて検証するようになるため、IT 部門が配布したルートが Python でも機能します。urllib3、requests、pip にも効果があり、pip は pip 24.2 以降デフォルトで truststore を使います。

    python
    # pip install truststore==0.10.5  — verify with the OS trust store (Keychain, Windows store, Linux bundle)
    import truststore
    truststore.inject_into_ssl()   # call once at start-up, before creating connections
    
    import urllib.request
    urllib.request.urlopen("https://api.example.com/")
  3. 3.

    1 つのクライアントに限って CA を明示的に読み込む

    プライベートなサービスと通信するコードでは、ssl.create_default_context() に cafile(または cadata)を渡してください。コンテキストは引き続きホスト名と日付を検証します。

    python
    import ssl, urllib.request
    
    ctx = ssl.create_default_context(cafile="/etc/ssl/private-ca/corp-root-ca.pem")
    urllib.request.urlopen("https://api.example.com/", context=ctx)
  4. 4.

    デフォルトのバンドルのパスを確認して設定する

    ssl.get_default_verify_paths() を実行すると、OpenSSL が使うファイルとディレクトリがわかります。SSL_CERT_FILE と SSL_CERT_DIR は、プロセス単位でこれらを上書きします。公開ルートと自社の CA の両方を含むバンドルを指定してください。

    bash
    # Where does this Python look by default?
    python3 -c "import ssl; print(ssl.get_default_verify_paths())"
    # Point OpenSSL-based clients at a bundle that includes your CA
    export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
  5. 5.

    サーバー管理者:完全なチェーンを送信する

    ハンドシェイクに証明書が 1 つしか含まれておらず、その発行者がパブリックな中間 CA である場合は、完全なチェーンを含むファイルをサーバーに設定してください。

    bash
    # Server side: 1 certificate in the handshake = intermediate missing
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
      | grep -c 'BEGIN CERTIFICATE'

関連するエラー

署名エラーデータベースをすべて見る