在小牛队的python中SSL证书验证失败

时间:2014-09-14 16:40:17

标签: python ssl osx-mavericks python-requests

我坚持持续的SSL验证问题。

SSL:CERTIFICATE_VERIFY_FAILED

我在构建一个让用户使用Mozilla Persona进行身份验证的Django应用时发现了这个错误。

(python3.4)> import requests
(python3.4)> requests.get('https://verifier.login.persona.org')

我从SSL: CERTIFICATE_VERIFY_FAILEDrequests再到urllib3进行ssl追踪:

...
"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/ssl.py", line 805, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:598)

...
"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/requests-2.4.1-py3.4.egg/requests/packages/urllib3/connectionpool.py", line 543, in urlopen
    raise SSLError(e)
requests.packages.urllib3.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:598)

...
"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/requests-2.4.1-py3.4.egg/requests/adapters.py", line 420, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:598)

python3和python2

之间的区别

这里开始变得有趣:使用python2.7时我没有遇到同样的问题:

(python2.7)> import requests
(python2.7)> requests.get('https://verifier.login.persona.org')
<Response [200]>

我的第一个想法是requests的两个版本可能使用不同的证书[1],所以我很惊讶地发现这两个文件完全相同:

(bash)$ diff `python3.4 -c "import requests; print(requests.certs.where())"` \
             `python2.7 -c "import requests; print requests.certs.where()"`
# no diff

在openssl中重新创建错误并使用-CAFile

解决

有趣的是,问题不仅限于python3.4 [2]。

(bash)$ openssl s_client -connect github.com:443
...
Verify return code: 20 (unable to get local issuer certificate)

编辑 Steffen的评论告诉我,这种“调试”方法实际上并不提供信息,因为s_client需要-CA路径才能验证。但是,我可以指定requests包使用的相同证书并且我没有得到相同的错误这一事实仍然很有趣:

(bash)$ openssl s_client -connect github.com:443 \
        -CAfile `python3 -c 'import requests; print(requests.certs.where())'`
...
Verify return code: 0 (ok)

此时,我完全脱离了我的元素。我不知道这是否真的是openssl问题,或者是关于OSX Mavericks的问题[3]。这是我正在使用的openssl的版本:

(bash)$ openssl version
OpenSSL 1.0.1f 6 Jan 2014

Mavericks KeyChain.app

对于特定于操作系统的解决方案,我尝试清除登录KeyChain [4],但无济于事。

pip的问题

最后一点证据可能相关或不相关。 python3.4带有完整的pip。但是,pip3命令对我来说没用。无论我尝试安装什么:

(bash)$ pip3 install [new-lib] # pip 1.5.6

我明白了:

Downloading/unpacking [new-lib]
    Cannot fetch index base URL https://pypi.python.org/simple/
    Could not find any downloads that satisfy the requirement [new-lib]
Cleaning up...
    No distributions at all found for [new-lib]
    Storing debug log for failure in ~/.pip/pip.log

虽然这不是(明确地)SSL错误,但看起来类似[5]并且成功的解决方法是在我的easy_install中使用virtualenv安装旧版本的pip [5] ]。我正在指责这两个问题是相关的。

回顾:

  • 寻求SSL证书失败错误的可能解决方案(在verify = False来电中不使用requests)。
  • 我在python3.4中得到错误但不是python2.7,即使两种情况下使用的cert.pem完全相同。
  • 虽然我可以使用openssl s_client -connect重新创建SSL错误,但我可以通过为请求库使用的cert.pem指定-CAFile来避免它。
  • 我最好的猜测是这是小牛特有的问题,但我不知道如何继续。
  • 我希望找到一个解决方案,让我可以使用pip3按预期安装python3.4软件包。

感谢您的帮助!

[1]:我的机器上的python2.7是使用Enthought安装的。但是安装系统版本的python2.7和请求库也可以。

[2]:使用python 2.7

查看类似问题的openssl, python requests error: "certificate verify failed"

[3]:小牛似乎引入了openssl的变化? http://curl.haxx.se/mail/archive-2013-10/0036.html

[4]:从这里清理KeyChain.app:https://superuser.com/a/721629/261875

[5]:pip3发生SSL错误:https://stackoverflow.com/a/22051466/2506078

2 个答案:

答案 0 :(得分:2)

根据您提供的其他信息,您似乎已安装仅限32位版本的Python 3.4.1 from python.org。此版本主要用于OS X 10.5系统;因此,它与Apple提供的OpenSSL版本10.5相关联。您可以使用python.org中的64位/ 32位3.4.1安装程序来避免此问题;此版本推荐用于OS X 10.6+,并与较新版本的Apple的OpenSSL链接。否则,您可以使用curl或浏览器从PyPI手动下载发行版,并安装pip从下载的文件中安装它们。

答案 1 :(得分:1)

猜测一下:Mac OS X附带的OpenSSL(仍然是0.9.8)中有特殊的钩子,因此如果验证对OpenSSL本身提供的CA失败,则会回退到OS X密钥环。但是,如果您使用自己的OpenSSL,它就没有这种后备。

这意味着,如果您使用内置的OpenSSL和python2,它将成功验证站点是否在OS X密钥环内找到CA,即使它不在请求本身提供的证书存储中。但是如果您已经针对自己的OpenSSL编译了python3,它将只使用请求本身提供的CA而不会回退到OS X密钥环,因此无法验证CA是否不在请求密钥环中。

有关Mac OS X此“功能”的详细信息及其引入的问题,请参阅https://hynek.me/articles/apple-openssl-verification-surprises/

不幸的是,这并没有解释为什么openssl成功验证了请求库的默认证书,除非涉及另一个OpenSSL版本,即python3使用的版本没有密钥环回退和命令行上的最新版本后备。