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

Tho*_*ner 23 python ssl debian debian-based python-requests

我已经在debian中安装了一个自签名的root ca证书/usr/share/ca-certificates/local并安装了它们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')],)",)
Run Code Online (Sandbox Code Playgroud)

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')],)",)
Run Code Online (Sandbox Code Playgroud)

为什么python会忽略系统ca-certificates包,我该如何集成它呢?

Tho*_*ner 49

来自/sf/answers/2360226221/

要使python请求使用系统ca-certificates包,需要告知它通过自己的嵌入式bundle使用它

export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
Run Code Online (Sandbox Code Playgroud)

请求将其捆绑包嵌入此处,以供参考:

/usr/local/lib/python2.7/site-packages/requests/cacert.pem
/usr/lib/python3/dist-packages/requests/cacert.pem
Run Code Online (Sandbox Code Playgroud)

  • 设置环境变量 REQUESTS_CA_BUNDLE 有效。但是,它不会更改 certifi 模块中的 crt 路径。答案意味着确实如此,但我在 python 3.7 和 3.8 中的测试显示情况并非如此。我建议使用 os.getenv 来检查路径。 (9认同)
  • 言语无法形容这个答案让我在解决一个我已经研究了几周的问题时感到多么高兴。 (7认同)
  • @rwarner 说了什么! (5认同)
  • 删除请求捆绑的默认cacert.pem后,请求似乎在不设置环境变量的情况下获取系统ca-certifications包. (4认同)
  • 该环境变量并不适用于所有系统。例如,在 CentOS 上,我需要使用 SSL_CERT_FILE 变量,如另一个答案中所述:/sf/answers/5274664041/ (2认同)

小智 19

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

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

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

Run Code Online (Sandbox Code Playgroud)

所以如果你使用apt-installedrequestscertifi没有问题了。

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

  • 我发现设置 SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt 确实可以与 aiohttp 一起使用。 (4认同)

Ric*_*rin 11

我的两分钱:

感谢另一个答案,它让我检查了实际的请求代码,我发现您不必使用 env 变量,而只需在您的请求中设置“验证”参数即可:

requests.get("https://whatever", verify="/my/path/to/cacert.crt", ...)
Run Code Online (Sandbox Code Playgroud)

它也被记录在案,尽管我只能在发现之后才能找到文档(并且 pypi 项目指向文档的死链接):D


fry*_*yad 7

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

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

将最后一个证书(根证书)复制到新的文本文件中。抓住介于两者之间的内容,包括:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Run Code Online (Sandbox Code Playgroud)

将中间证书(又名中间证书)复制到根证书下的新文本文件中。同样,抓住“开始和结束证书”行以及之间的所有内容。

将此文本文件保存到Python脚本所在的目录中。我的建议是打电话CertBundle.pem。(如果使用其他名称,或将其放在文件夹结构中的其他位置,请确保验证行反映了这一点。)更新脚本以引用新的证书捆绑包:

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

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

编辑:修复了证书捆绑包的文件扩展名。此外,还修复了一些语法错误。

  • @fryad是正确的;该文件应该具有`.pem`扩展名,并且某些工具会错误处理该文件,因为其扩展名错误。.pem是此base64编码的证书格式的[_de facto_标准扩展名](https://serverfault.com/a/9717/322144),而相同格式的二进制版本是.der。 `.p7b`是_different_ base64编码的格式。关于如何使用“ openssl” CLI工具在它们之间进行转换的方便参考:https://knowledge.digicert.com/solution/SO26449.html (3认同)
  • 我不确定 .p7b 是该捆绑包的正确语义扩展。(尽管我并不是真正的专家)只是习惯于看到 .pem 和 .crt 用于 CA 捆绑包。我知道 debian ca-certificates 包对要添加到系统提供的证书存储中的 .crt 扩展名的证书很挑剔。 (2认同)

Bol*_*lli 5

在尝试了一切之后,我发现这在 Ubuntu 上对我有用

export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
Run Code Online (Sandbox Code Playgroud)

即使certifi显示了相同的路径,我也必须这样做。