跳到主要内容
ecdsa.com
requests / urllib3 (Python)Python cryptography 库

错误信息

Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

也可能输出为

  • requests.exceptions.SSLError: HTTPSConnectionPool(host='api.example.com', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

主机、端口和 URL 路径取自你的请求。最内层的原因各不相同(“self-signed certificate in certificate chain”、“Hostname mismatch…”、“certificate has expired”),这一行以源代码位置结尾,例如“(_ssl.c:1082)')))”。

requests / urllib3 (Python) — requests.exceptions.SSLError wrapping urllib3 MaxRetryError

含义

requests 向 urllib3 请求连接,TLS 握手时证书验证失败,于是 urllib3 放弃了(requests 默认不重试,所以在第一次尝试后就放弃)。有用的信息在括号里:这里 OpenSSL 在 requests 使用的 CA 证书包中找不到颁发者,而这个证书包默认是 certifi 提供的 Mozilla 根证书副本,不是操作系统的证书存储。

原因

修复方法

  1. 1.

    通过 verify= 传入正确的证书包

    verify 接受 CA 证书包文件的路径,或按哈希命名的 CA 目录的路径;主机名和日期检查仍然保持开启。

    python
    import requests
    
    # A CA file (or a directory prepared with c_rehash) for this call
    requests.get("https://api.example.com/", verify="/etc/ssl/certs/ca-certificates.crt")
  2. 2.

    为进程设置 REQUESTS_CA_BUNDLE

    把它指向系统证书包(先把你的 CA 安装进去),该进程中的每次 requests 调用就会与 curl 和操作系统工具信任相同的根证书。

    bash
    # For every requests call in the process (also honoured: CURL_CA_BUNDLE)
    export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt   # Debian/Ubuntu system bundle
    python3 app.py
  3. 3.

    使用操作系统的证书存储

    在启动时调用 truststore.inject_into_ssl(),requests 就会通过操作系统进行验证,这样 IT 部门部署的根证书无需维护证书包即可生效。

    python
    # pip install truststore==0.10.5 — requests then verifies with the OS trust store
    import truststore
    truststore.inject_into_ssl()
    
    import requests
    requests.get("https://api.example.com/")
  4. 4.

    只配置一次 Session 会话

    对于应用程序,把 Session.verify 设置为同时包含公共根证书和公司根证书的证书包,并复用该会话。

    python
    import requests
    
    session = requests.Session()
    session.verify = "/etc/ssl/private-ca/corp-ca-bundle.pem"   # public roots + company root
    session.get("https://api.example.com/")

相关错误

浏览完整的签名错误库