错误信息
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain
也可能输出为
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain
OpenSSL 3 打印“self-signed”,OpenSSL 1.1.1 打印“self signed”;Python 会附加源代码位置,例如“(_ssl.c:1082)”。通过 urllib 调用时,它出现在“<urlopen error …>”中;通过 requests 调用时,则出现在“Max retries exceeded … (Caused by SSLError(…))”中。
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN (19)
含义
服务器提供的证书链终止于一张自签名根证书,而它不在这个 Python 进程信任的 CA 之中。证书链本身是完整的,只是客户端不认识这个根证书。如果访问 pypi.org 或 api.github.com 这类公共网站时出现这个错误,这个根证书就属于你所在网络或你机器上执行 TLS 检查的代理或杀毒软件。
原因
企业代理或杀毒软件拦截了 HTTPS
常见IT 部门把拦截用的根证书安装到了 Windows 或 macOS 的证书存储中,所以浏览器能正常工作。而 Python 的 OpenSSL 以及使用 certifi 证书包的库,都不读取这些证书存储。
requests、pip 或 httpx 使用 certifi 进行验证
常见certifi 只包含 Mozilla 的公共根证书。把公司根证书添加到系统证书存储中并不会改变 certifi 的文件,而用 pip 升级 certifi 会覆盖手动修改。
内部服务发送了自己的私有根证书
偶见使用公司 CA 签发证书的内部 API,即使在证书链中附带了根证书,仍然需要在客户端上配置该根证书。
conda、pyenv 或虚拟环境使用自己的证书包
偶见conda 自带 OpenSSL 和 CA 证书包;对系统 Python 所做的修复不会作用到它。
修复方法
- 1.
查明这是谁的根证书
输出某个公共主机的证书链。如果根证书以你的公司或某个安全产品命名,它就是应该信任的那个;如果在不受信任的网络上看到陌生的名称,就应该停下来,而不是信任它。
bash openssl s_client -connect pypi.org:443 -servername pypi.org -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:' # A top certificate named after your company or a security vendor = interception proxy - 2.
使用操作系统的证书存储进行验证
truststore.inject_into_ssl() 让 Python 的 TLS 使用操作系统的证书存储,其中已经包含你的组织部署的根证书。在受管理的 Windows 和 macOS 机器上,这是最干净的修复方式。
python # pip install truststore==0.10.5 import truststore truststore.inject_into_ssl() # the proxy root deployed in the OS store is now trusted import requests requests.get("https://api.example.com/") - 3.
构建包含公共根证书和你的根证书的证书包
REQUESTS_CA_BUNDLE(requests)和 SSL_CERT_FILE(ssl、urllib、httpx)会替换默认的证书包文件,所以要把 certifi 的根证书与公司根证书拼接在一起,而不是只指向公司根证书。
bash # Keep the public roots and add the company/proxy root cat "$(python3 -m certifi)" corp-root-ca.pem > ~/.config/corp-ca-bundle.pem export REQUESTS_CA_BUNDLE=~/.config/corp-ca-bundle.pem # requests export SSL_CERT_FILE=~/.config/corp-ca-bundle.pem # ssl, urllib, httpx - 4.
conda:配置它自己的设置
conda 从 .condarc 中读取 ssl_verify;把它设为合并后的证书包,conda install 和 conda update 就能在代理后面正常工作。
bash # conda keeps its own OpenSSL and CA bundle conda config --set ssl_verify /path/to/corp-ca-bundle.pem
相关错误
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local…Python ssl module (OpenSSL)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certifi…Python ssl module (OpenSSL)There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FA…pipcurl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)