跳到主要内容
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 使用的 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 使用操作系统的证书存储

    truststore 让 ssl.SSLContext 通过操作系统进行验证(macOS 上的钥匙串、Windows 证书存储、Linux 上的系统证书包),这样 IT 部门部署的根证书在 Python 中也能生效。它同样适用于 urllib3、requests 和 pip,而 pip 从 24.2 版本起默认就使用它。

    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.

    为单个客户端显式加载 CA

    对于与私有服务通信的代码,把 cafile(或 cadata)传给 ssl.create_default_context()。该上下文仍会验证主机名和日期。

    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.

    服务器所有者:发送完整的证书链

    如果握手中只有一张证书,而它的颁发者是公共中间证书,就为服务器配置完整证书链文件。

    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'

相关错误

浏览完整的签名错误库