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

89

我需要在公司内部网络使用自定义根证书,并将它们加载到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 支持。


1
相关问题:https://dev59.com/t14c5IYBdhLWcg3wgqn2 - 0 _
我们为什么需要这些证书呢?这难道不会让您面临安全漏洞吗? - JGFMK
10个回答

91

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

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包

pip install certifi

我选择了第一个选项,它起作用了。


还有一种选择是从Homebrew获取Python,它会自动为您完成。 - Giacomo Lacava
太棒了!这也帮助我解决了一个ansible问题:https://github.com/ansible/ansible/issues/50777 - lifeofguenter
24
这个解决方案在使用Python 3.7的OSX Catalina系统中已经失效。/Applications/Python 3.7目录不存在,并且安装证书命令也不可用。安装证书也不能解决这个问题。现在被卡住了!有人在Catalina上解决了这个问题吗? - RandallShanePhD
2
这个可以运行。如果您有自定义的CA证书,请将它们添加到/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/certifi/cacert.pem,然后重新运行/Applications/Python\ 3.7/Install\ Certificates.command。 - Andy Fraley
4
我认为这个解决方案不错,但对于使用pyenv或直接构建Python的人来说还不够。在安装了certifi之后,我们需要遵循https://dev59.com/UFkR5IYBdhLWcg3w4hDN#57795811的步骤。 - Andrew Chong
显示剩余3条评论

87
运行此命令以设置相应的变量。这是已经在此处给出的答案的组合。将其放在您的~/.bash_profile中,以使其永久化。

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

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

4
太好了!这个答案对于解决我在Python 3.8虚拟环境设置中遇到的问题非常相关,其中Jupyter笔记本遇到证书错误。我在环境内使用了“.bash_profile”文件,然后问题就解决了。 - Shanti
@Shanti,看起来你已经解决了我正在苦恼的问题...请快速浏览一下我的问题:https://dev59.com/pr3pa4cB1Zd3GeqPoPDm?noredirect=1#comment114209320_64590535 - Madamadam
11
对我没用。在Mac OS Big Sur 11.2.3上通过brew安装了Python 3.9.2。 - Marcello Romani
@MarcelloRomani 我使用的是Mac OS Big Sur 11.2.3,而且成功了。 - Justo
谢谢,对我有用!我在我的Mac上运行从源代码安装的Python 3.8.14版本。 - progfan
显示剩余2条评论

22

在 Mac 上安装 Python 环境,请使用 brew install 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'
>>> 

或者从命令行中运行:

$ python -m certifi

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

然后正常工作。


尝试了很多方法,但只有这一个方案奏效了。赞! - Nishanta Khanal
对我有用,谢谢! - Paweł
这个对我也起作用了,非常感谢,我花了一周时间寻找解决方案。 - Luke_V
如果你想知道该链接哪个文件,可以使用命令 python -c "import ssl; print(ssl.get_default_verify_paths())",从输出中找到列出的 openssl_cafile 值。 - not2savvy
如果 rehash 无法使用(命令未找到),请改用 c_rehash - not2savvy

21
这是对现有问题的编辑,但由于队列已满,因此作为单独的答案发布。
在MacOS 12.3.1上测试,使用MacPorts安装了python 3.10。
如果您希望根据操作系统信任根CA,请将它们从System Roots钥匙串导出到单个文件中:
security export -t certs -f pemseq -k /System/Library/Keychains/SystemRootCertificates.keychain -o bundleCA.pem

如果您想信任一些内部自签名的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仅适用于单个文件,而不适用于目录。

这两个“security export”命令是相同的,我认为这一定是一个打字错误。有没有办法在Mac上找到内部CA的位置? - ewan.chalmers
@sudocode:确实。已经更正了。感谢指出! - badbishop
4
我在网上搜寻了很久才找到一个可行的解决方案,这正是我所需要的。 - mh00h

16

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

SSL_CERT_FILE=/System/Library/OpenSSL/cert.pem
REQUESTS_CA_BUNDLE=/System/Library/OpenSSL/cert.pem
请注意,此文件不存在,您需要自行构建。

1
我的证书位于 /Library/Frameworks/Python.framework/Versions/2.7/etc/openssl/cert.pem。 - Tzach Solomon
// 将 REQUESTS_CA_BUNDLE 设置为我生成的堆叠 .pem,解决了我的使用Requests访问内部根CA的问题。感谢@Sorin。 - Nathan Basanese

7

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

PEP描述了为什么这样做是错误的。 ssl.create_default_context与Linux和Windows信任库很好地集成。问题在于,就像您在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())

请注意,这将给您系统根证书。如果您需要用户证书,则只需更改安全命令中的值。

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)

解决问题的指南在/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%]
...

1

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

所以这是我做的:

  1. 在shell中手动运行pip3 install --update certify
  2. 从命令脚本中删除安装certifi的行
  3. 重新运行脚本,一切正常。

请注意,您将在以下位置找到cert.pem符号链接:/Library/Frameworks/Python.framework/Versions/3.6/etc/openssl/


1
在我的情况下,只有安装“Install Certificates.command”才能解决这个问题,使用MAC OS

使用certifi更新SSL证书(仅适用于MacOS)

我们所要做的就是运行以下代码片段的命令:

- Press "command + space" button or open Spotlight
- type "Install Certificates.command" 


这个命令的作用是更新我们系统在MacOS上的SSL证书目录。

1

适用于已安装最新Python版本的MacOS或Linux解决方案,可以作为独立软件包或通过端口或brew安装

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

然后添加到您的~/.zshrc(适用于最新的MacOS)或~/.bash_profile或类似文件中:

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

1
Python的requests库依赖于certifi库,并默认使用它的证书,无需将其添加到环境变量路径中。 - Matan Benita
谢谢igo,在Mac上,这解决了openssl无法构建的问题,因为mac证书未在python venv中加载。 - Cobra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接