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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42982143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 22:21:57  来源:igfitidea点击:

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

pythonssldebianpython-requestsdebian-based

提问by ThorSummoner

I've installed a self-signed root ca cert into debian's /usr/share/ca-certificates/localand installed them with sudo dpkg-reconfigure ca-certificates. At this point true | gnutls-cli mysite.localis happy, and true | openssl s_client -connect mysite.local:443is happy, but python2 and python3 requests module insists it is not happy with the cert.

我已经在 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:

蟒蛇2:

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

蟒蛇3

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')],)",)

Why does python ignore the system ca-certificates bundle, and how do I integrate it?

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

回答by ThorSummoner

From https://stackoverflow.com/a/33717517/1695680

来自https://stackoverflow.com/a/33717517/1695680

To make python requests use the system ca-certificates bundle, it needs to be told to use it over its own embedded bundle

要使 python 请求使用系统 ca-certificates 包,需要告诉它在自己的嵌入式包上使用它

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


Requests embeds its bundles here, for reference:

Requests 在这里嵌入了它的包,以供参考:

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

回答by fryad

I struggled with this for a week or so recently. I finally found that the way to verify a self-signed, or privately signed, certificate in Python. You need to create your own certificate bundle file. No need to update obscure certificate bundles every time you update a library, or add anything to the system certificate store.

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

Start by running the openssl command that you ran before, but add -showcerts. openssl s_client -connect mysite.local:443 -showcertsThis will give you a long output, and at the top you'll see the entire certificate chain. Usually, this means three certs, the website's certificate, the intermediate certificate, and the root certificate in that order. We need to put just the root and intermediate certificates into a next file in the opposite order.

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

Copy the last cert, the root certificate, to a new text file. Grab just the stuff between, and including:

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

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

Copy the middle cert (aka the intermediate certificate) to the new text file under the root cert. Again, grab the Begin and End Certificate lines and everything in between.

将中间证书(又名中间证书)复制到根证书下的新文本文件中。再次,获取开始和结束证书行以及其间的所有内容。

Save this text file to the directory where your Python script resides. My recommendation is to call it CertBundle.pem. (If you give it a different name, or put it somewhere else in your folder structure, make sure that the verify line reflects that.) Update your script to reference the new certificate bundle:

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

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

And that's it. If you have only the root or only the intermediate certificate, then Python can't validate the entire certificate chain. But, if you include both of the certificates in the certificate bundle that you created, then Python can validate that the intermediate was signed by the root, and then when it accesses the website it can validate that the website's certificate was signed by the intermediate certificate.

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

edit: Fixed the file extension for the cert bundle. Also, fixed a couple of grammatical mistakes.

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

回答by Riccardo Manfrin

My two cents:

我的两分钱:

Thanks to this other answer, which had me check on actual requests code, I figured out that you don't have to use the env variable but can just set the "verify" param in your request:

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

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

It is also documented, although I could only find the documentation after having made the discovery (and the pypi project points to a dead link for doc) :D

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