简体   繁体   English

如何使 Python 使用来自 Mac OS TrustStore 的 CA 证书?

[英]How to make Python use CA certificates from Mac OS TrustStore?

I need to use curtom root certificates on the company intr.net and loading them in the Mac OS TrustStore (KeyChain) does solve the problem for all browsers and GUI apps.我需要在公司 intr.net 上使用 curtom root 证书并将它们加载到 Mac OS TrustStore (KeyChain) 中确实解决了所有浏览器和 GUI 应用程序的问题。

It seems that it works even with the version of curl that ships with Mac OS X but it doesn't work with python , even the version that ships with Mac OS 10.12 Sierra (Python 2.7.10)它似乎甚至适用于 Mac OS X 附带的curl版本,但不适用于 python ,即使是 Mac OS 10.12 Sierra(Python 2.7.10)附带的版本

Still, it seems that I would be hit by:不过,我似乎会受到以下打击:

urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)>

How can I solve this?我该如何解决这个问题?

Because I encounter this issue in lots and lots of Python tools I would really appreciate if I find a way to avoid it without having to patch them.因为我在很多 Python 工具中遇到过这个问题,如果我能找到一种无需修补它们的方法来避免它,我将不胜感激。

Providing the custom CA certificate myself is not an option because I cannot patch tens of Python tools that I use.自己提供自定义 CA 证书不是一种选择,因为我无法修补我使用的数十个 Python 工具。

Most of the tools are using the requests library but there are a few that are using the native ssl support in Python directly.大多数工具都使用requests库,但有一些工具直接使用 Python 中的本机 ssl 支持。

This is also a problem in Python 3.6 with MacOS Sierrra.这也是 MacOS Sierrra 的 Python 3.6 中的一个问题。 I know your use case is different.我知道你的用例是不同的。 But I stumbled upon this thread while investigating this problem.但是我在调​​查这个问题时偶然发现了这个线程。 So if anyone is also having this article is worth checking out:因此,如果有人也有这篇文章值得一看:

http://www.cdotson.com/2017/01/sslerror-with-python-3-6-x-on-macos-sierra/ http://www.cdotson.com/2017/01/sslerror-with-python-3-6-x-on-macos-sierra/

In a nutshell : Python 3.6 does not rely on MacOS' openSSL anymore.简而言之:Python 3.6 不再依赖 MacOS 的 openSSL。 It comes with its own openSSL bundled and doesn't have access on MacOS' root certificates.它捆绑了自己的 openSSL,并且无法访问 MacOS 的根证书。

You have two options:您有两个选择:

Run an install command shipped with Python 3.6运行 Python 3.6 附带的安装命令

cd /Applications/Python\ 3.6/
./Install\ Certificates.command

or或者

Install the certifi package with安装证书包

pip install certifi

I chose the first option and it worked.我选择了第一个选项,它奏效了。

Run this to set the appropriate variables.运行它以设置适当的变量。 This is a combination of the answers that have already been given here.这是这里已经给出的答案的组合。 Put it in your ~/.bash_profile to make it permanent.把它放在你的 ~/.bash_profile 中以使其永久化。

CERT_PATH=$(python -m certifi)
export SSL_CERT_FILE=${CERT_PATH}
export REQUESTS_CA_BUNDLE=${CERT_PATH}

Mac brew install python env. Mac brew 安装 python 环境。

$ python3
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 16:52:21) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import certifi
>>> certifi.where()
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/certifi/cacert.pem'
>>> 

Or from the command line:或者从命令行:

$ python -m certifi

then need link cacert.pem as cert.pem然后需要将 cacert.pem 链接为 cert.pem

$ ln -s /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/certifi/cacert.pem cert.pem
$ pwd
/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl

rehash

then work fine.然后工作正常。

If you put the additional certificates in a PEM bundle file you can use these two environment variables to overwrite the default cert stores used by Python openssl and requests.如果将附加证书放在 PEM 包文件中,则可以使用这两个环境变量覆盖 Python openssl 和 requests 使用的默认证书存储。

SSL_CERT_FILE=/System/Library/OpenSSL/cert.pem
REQUESTS_CA_BUNDLE=/System/Library/OpenSSL/cert.pem

Please note that this file does not exist, you need to build it yourself.请注意,此文件不存在,您需要自行构建。

This was intended as an edit to an existing question, but since the queue was already full, posting as a separate answer.这是对现有问题的编辑,但由于队列已经满了,因此作为单独的答案发布。

Tested on MacOS 12.3.1 with python 3.10 installed with MacPorts.在安装了 MacPorts 的 python 3.10 的 MacOS 12.3.1 上测试。

If you prefer to trust root CA according to your OS, export them from System Roots keychain into a single file:如果您更愿意根据您的操作系统信任根 CA,请将它们从系统根钥匙串导出到一个文件中:

security export -t certs -f pemseq -k /System/Library/Keychains/SystemRootCertificates.keychain -o bundleCA.pem

If, additionally, you want to trust some internal self-signed CAs, export them, too.此外,如果您想信任某些内部自签名 CA,也可以导出它们。 They are probably stored under the System keychain:它们可能存储在系统钥匙串下:

security export -t certs -f pemseq -k /Library/Keychains/System.keychain -o selfSignedCAbundle.pem

Merge the two files:合并两个文件:

cat bundleCA.pem selfSignedCAbundle.pem >> allCAbundle.pem

Export as bash variable导出为bash变量

export REQUESTS_CA_BUNDLE=/path/to/allCAbundle.pem

Consider adding the last code snippet to your .bash_profile考虑将最后一个代码片段添加到您的.bash_profile

Note that REQUESTS_CA_BUNDLE works only for a single file, and does not for a directory.请注意, REQUESTS_CA_BUNDLE仅适用于单个文件,不适用于目录。

As an update and datapoint, I ran into this issue running Python 3.7.0 on macOS 10.13.4:作为更新和数据点,我在 macOS 10.13.4 上运行 Python 3.7.0 时遇到了这个问题:

$ ipython
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.0.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import bokeh.sampledata

In [2]: bokeh.sampledata.download()
Using data directory: /Users/me/.bokeh/data

...
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1045)

Instructions for solving the problem are in /Applications/Python\\ 3.7/ReadMe.rtf解决问题的说明在/Applications/Python\\ 3.7/ReadMe.rtf

Following the suggestion there and running /Applications/Python\\ 3.7/Install\\ Certificates.command solved the problem:按照那里的建议并运行/Applications/Python\\ 3.7/Install\\ Certificates.command解决了问题:

From the terminal:从终端:

$ /Applications/Python\ 3.7/Install\ Certificates.command

Re-starting IPython...重新启动 IPython...

$ ipython
>>> import bokeh.sampledata

>>> bokeh.sampledata.download()
Using data directory: /Users/me/.bokeh/data
Downloading: CGM.csv (1589982 bytes)
   1589982 [100.00%]
...

For me /Applications/Python\\ 3.6/./Install\\ Certificates command fails on pip certifi install.对我来说, /Applications/Python\\ 3.6/./Install\\ Certificates命令在 pip certifi 安装时失败。 I am on mac High Sierra and use python3 so pip somewhat fails and I have to use pip3 instead.我在 mac High Sierra 上使用 python3,所以 pip 有点失败,我必须改用 pip3。

So here what I did:所以在这里我做了什么:

  1. Manually ran pip3 install --update certify in a shell在 shell 中手动运行pip3 install --update certify
  2. Remove the install certifi line from the command script从命令脚本中删除安装证书行
  3. Reran the script and everything was fine.重新运行脚本,一切都很好。

Note that you will end up with a cert.pem symbolic link in: /Library/Frameworks/Python.framework/Versions/3.6/etc/openssl/请注意,您将在以下位置获得 cert.pem 符号链接: /Library/Frameworks/Python.framework/Versions/3.6/etc/openssl/

I see a lot of answers out there recommend to turn off certificate validation or to use certifi.where .我看到很多答案都建议关闭证书验证或使用certifi.where While turning off SSL is obvious risk.而关闭 SSL 是明显的风险。 certifi.where is also a risk, mainly if you intend to make this code a production code that will run in a customer env. certifi.where也是一个风险,主要是如果您打算将此代码制作为将在客户环境中运行的生产代码。

PEP describing why it is wrong. PEP描述错误的原因。 ssl.create_default_context is well integrate with linux and windows trustStore. ssl.create_default_context与 linux 和 windows trustStore 很好地集成。 the problem is, as in your case with mac.问题是,就像你在 mac 上的情况一样。 I solve this by loading the certificates using the integrated security commandline tool我通过使用集成的安全命令行工具加载证书来解决这个问题

def create_macos_ssl_context():
    import subprocess
    import ssl
    import tempfile
    ctx = ssl.create_default_context()
    macos_ca_certs = subprocess.run(["security", "find-certificate", "-a", "-p",
                                     "/System/Library/Keychains/SystemRootCertificates.keychain"],
                                    stdout=subprocess.PIPE).stdout

    with tempfile.NamedTemporaryFile('w+b') as tmp_file:
        tmp_file.write(macos_ca_certs)
        ctx.load_verify_locations(tmp_file.name)
    print(ctx.get_ca_certs())

Note that this gives you the systemRoot certificates.请注意,这为您提供了 systemRoot 证书。 if you need the user than simply change the value in the security command如果您需要用户而不是简单地更改安全命令中的值

Solution for MacOS or Linux with latest Python versions installed either as standalone or via port or brew具有最新 Python 版本的 MacOS 或 Linux 解决方案作为独立安装或通过端口或 brew 安装

Download certificates from Certifi project at https://github.com/certifi/python-certifi/blob/master/certifi/cacert.pem .https://github.com/certifi/python-certifi/blob/master/certifi/cacert.pem从 Certifi 项目下载证书。 FYI Certifi is a 3rd party library that provides Mozilla's curated collection of Root Certificates for validating the trustworthiness of SSL certificates while verifying the identity of TLS hosts. FYI Certifi 是一个 3rd 方库,它提供 Mozilla 精选的根证书集合,用于在验证 TLS 主机身份的同时验证 SSL 证书的可信度。

Then add to your ~/.zshrc on the latest MacOS or ~/.bash_profile or similar:然后在最新的 MacOS 或~/.bash_profile或类似文件中添加到您的~/.zshrc

export SSL_CERT_FILE=/pathtodownloadedfile/cacert.pem
export REQUESTS_CA_BUNDLE=/pathtodownloadedfile/cacert.pem

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM