如何让 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。
所以这是我所做的:
- 在 shell
中手动 运行 pip3 install --update certify
- 从命令脚本中删除 install certifi 行
- 重新运行脚本,一切都很好。
请注意,您最终会得到一个 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
仅适用于单个文件,不适用于目录。
我需要在公司内部网上使用 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 packagepip install certifi
我选择了第一个选项,它成功了。
对我来说 /Applications/Python\ 3.6/./Install\ Certificates
命令在 pip certifi 安装上失败。我在 mac High Sierra 并使用 python3 所以 pip 有点失败,我必须改用 pip3。
所以这是我所做的:
- 在 shell 中手动 运行
- 从命令脚本中删除 install certifi 行
- 重新运行脚本,一切都很好。
pip3 install --update certify
请注意,您最终会得到一个 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
仅适用于单个文件,不适用于目录。