Python 请求 - 如何使用系统 ca 证书 (debian/ubuntu)?

Python Requests - How to use system ca-certificates (debian/ubuntu)?

我已经在 debian 的 /usr/share/ca-certificates/local 中安装了一个自签名根 ca 证书,并使用 sudo dpkg-reconfigure ca-certificates 安装了它们。此时 true | gnutls-cli mysite.local 很高兴,true | openssl s_client -connect mysite.local:443 很高兴,但是 python2 和 python3 请求模块坚持认为它对证书不满意。

python2:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

python3

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/bin/python3.5/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

为什么 python 忽略系统 ca 证书包,我该如何集成它?

来自

要使 python 请求使用系统 ca 证书捆绑包,需要告知它在其自己的嵌入式捆绑包上使用它

export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

Requests 在此处嵌入其捆绑包,以供参考:

/usr/local/lib/python2.7/site-packages/requests/cacert.pem
/usr/lib/python3/dist-packages/requests/cacert.pem

或者在较新的版本中使用附加包从以下位置获取证书: https://github.com/certifi/python-certifi

要验证从哪个文件加载证书,您可以尝试:

Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
>>> import certifi
>>> certifi.where()
'/etc/ssl/certs/ca-certificates.crt'

最近我为此苦苦挣扎了一个星期左右。我终于在 Python 中找到了验证自签名或私人签名证书的方法。您需要创建自己的证书捆绑文件。无需在每次更新库或向系统证书存储中添加任何内容时更新晦涩的证书包。

从 运行 您之前 运行 的 openssl 命令开始,但添加 -showcerts。 openssl s_client -connect mysite.local:443 -showcerts 这会给你一个长输出,在顶部你会看到整个证书链。通常,这意味着三个证书,依次为网站证书、中间证书和根证书。我们只需要将根证书和中间证书以相反的顺序放入下一个文件中。

将最后一个证书(根证书)复制到一个新的文本文件中。抓住之间的东西,包括:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

将中间证书(也称为中间证书)复制到根证书下的新文本文件中。再次获取 Begin 和 End Certificate 行以及两者之间的所有内容。

将此文本文件保存到 Python 脚本所在的目录。我的建议是称它为CertBundle.pem。 (如果你给它一个不同的名字,或者把它放在你的文件夹结构的其他地方,确保验证行反映了这一点。)更新你的脚本以引用新的证书包:

response = requests.post("https://www.example.com/", headers=headerContents, json=bodyContents, verify="CertBundle.pem")

就是这样。如果您只有根证书或只有中间证书,那么 Python 无法验证整个证书链。但是,如果您在创建的证书包中包含这两个证书,则 Python 可以验证中间证书是否由根签名,然后当它访问该网站时,它可以验证该网站的证书是否已签名通过中级证书。

编辑:修复了证书包的文件扩展名。另外,修正了几个语法错误。

我的两分钱:

多亏了另一个 answer,它让我检查了实际的请求代码,我发现您不必使用 env 变量,而只需设置 "verify" 参数您的要求:

requests.get("https://whatever", verify="/my/path/to/cacert.crt", ...)

它也是documented,虽然我只有在发现之后才能找到文档(并且pypi项目指向一个死link for doc):D

requests使用certifi作为默认的根证书包,它内置了很多好的CA但无法修改。

Debian(和 Ubuntu)维护者更改了 certifi 与默认行为不同的行为:

def where():
    return "/etc/ssl/certs/ca-certificates.crt"

所以如果你使用 apt-installed requestscertifi 就没有问题。

但是 pip3 在虚拟环境中安装了 certifi 使用内置 CA。所以无法使用update-ca-certificates机制。除了在应用程序代码中手动指定根证书(如果通过 3rd 方接口间接调用 request 可能无法实现),它还可以覆盖环境变量 REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt 以模拟 Debianized 行为。

终于找到了可行的解决方案。所有其他方法均失败。

sudo apt install ca-certificates
sudo update-ca-certificates --fresh
export SSL_CERT_DIR=/etc/ssl/certs