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

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

我需要在公司内部网上使用 curtom 根证书并将它们加载到 Mac OS TrustStore (KeyChain) 确实解决了所有浏览器和 GUI 应用程序的问题。

它似乎甚至适用于 Mac OS X 附带的 curl 版本,但它 不适用于 python,甚至 Mac OS 10.12 Sierra (Python 2.7.10)

附带的版本

不过,我好像会中招:

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

我该如何解决这个问题?

因为我在很多 Python 工具中遇到过这个问题,如果我能找到一种无需修补它们的方法来避免它,我将不胜感激。

自己提供自定义 CA 证书不是一种选择,因为我无法修补我使用的数十个 Python 工具。

大多数工具都使用 requests 库,但有一些工具直接使用 Python 中的本机 ssl 支持。

如果您将额外的证书放入 PEM 捆绑文件中,您可以使用这两个环境变量来覆盖 Python openssl 和请求使用的默认证书存储。

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

请注意,该文件不存在,您需要自行构建。

这也是 Python MacOS Sierrra 3.6 中的一个问题。我知道你的用例是不同的。但是我在调​​查这个问题时偶然发现了这个线程。所以如果有人也有这篇文章值得一看:

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

简而言之:Python 3.6 不再依赖 MacOS 的 openSSL。它捆绑了自己的 openSSL,无法访问 MacOS 的根证书。

您有两个选择:

运行 Python 3.6

附带的安装命令
cd /Applications/Python\ 3.6/
./Install\ Certificates.command

安装certifi package
pip install certifi

我选择了第一个选项,它成功了。

对我来说 /Applications/Python\ 3.6/./Install\ Certificates 命令在 pip certifi 安装上失败。我在 mac High Sierra 并使用 python3 所以 pip 有点失败,我必须改用 pip3。

所以这是我所做的:

  1. 在 shell
  2. 中手动 运行 pip3 install --update certify
  3. 从命令脚本中删除 install certifi 行
  4. 重新运行脚本,一切都很好。

请注意,您最终会得到一个 cert.pem 符号 link 在:/Library/Frameworks/Python.framework/Versions/3.6/etc/openssl/

作为更新和数据点,我 运行 进入这个问题 运行 Python macOS 10.13.4 上的 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)

解题说明在/Applications/Python\ 3.7/ReadMe.rtf

按照那里的建议 运行 /Applications/Python\ 3.7/Install\ Certificates.command 解决了问题:

来自终端:

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

正在重新启动IPython...

$ ipython
>>> import bokeh.sampledata

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

Mac brew install python env.

$ 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'
>>> 

或从命令行:

$ python -m certifi

然后需要 link 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

那就好好工作吧。

运行 这个要设置相应的变量。这是此处已经给出的答案的组合。将其放入您的 ~/.bash_profile 以使其永久化。

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

我看到很多答案建议关闭证书验证或使用 certifi.where。 虽然关闭 SSL 是明显的风险。 certifi.where 也是一种风险,主要是如果您打算使此代码成为将 运行 在客户环境中的生产代码。

PEP描述错误的原因。 ssl.create_default_context 与 linux 和 windows trustStore 很好地集成。问题是,就像你 mac 的情况一样。 我通过使用集成安全命令行工具

加载证书来解决这个问题
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())

请注意,这会为您提供 systemRoot 证书。如果您需要用户而不是简单地更改安全命令中的值

适用于 MacOS 或 Linux 的解决方案,最新 Python 版本安装为独立或通过端口或 brew

https://github.com/certifi/python-certifi/blob/master/certifi/cacert.pem 的 Certifi 项目下载证书。 FYI Certifi 是一个第三方库,它提供 Mozilla 精选的根证书集合,用于在验证 TLS 主机身份的同时验证 SSL 证书的可信度。

然后在最新的 MacOS 或 ~/.bash_profile 或类似系统上添加到您的 ~/.zshrc

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

这本来是对现有问题的编辑,但由于队列已满,因此作为单独的答案发布。

在 MacOS 12.3.1 上测试,python 3.10 安装了 MacPorts。

如果您更愿意根据您的 OS 信任根 CA,请将它们从系统根钥匙串导出到单个文件中:

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

此外,如果您想信任某些内部 self-signed CA,也可以导出它们。它们可能存储在系统钥匙串下:

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

合并两个文件:

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

导出为 bash 变量

export REQUESTS_CA_BUNDLE=/path/to/allCAbundle.pem

考虑将最后一个代码片段添加到您的 .bash_profile

请注意 REQUESTS_CA_BUNDLE 仅适用于单个文件,不适用于目录。