Python请求SSL证书验证失败,即使添加了CA证书。

6
我一直使用Python Requests库爬取一个网站,但该网站最近更换了SSL证书,新的证书无法通过请求进行验证。
根据类似问题的答案,我已将requests和urllib3更新到最新版本(2.4.3和1.9.1),并手动将CA证书添加到requests的cacert.pem(/usr/local/lib/python2.7/dist-packages/requests/cacert.pem)中。
我可以成功地用curl使用这个cacert.pem文件,但还是无法在requests中使用。
> curl --head --cacert /usr/local/lib/python2.7/dist-packages/requests/cacert.pem
https://jordan-cu.org

HTTP/1.1 200 OK
Date: Thu, 20 Nov 2014 16:21:28 GMT
Server: Apache
X-Pingback: https://jordan-cu.org/xmlrpc.php
Link: <https://jordan-cu.org/>; rel=shortlink
X-Powered-By: PleskLin
Content-Type: text/html; charset=UTF-8

> python
Python 2.7.4 (default, Sep 26 2013, 03:20:26)
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> requests.get('https://jordan-cu.org')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 60, in get
    return request('get', url, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 49, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 420, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: [Errno 1] _ssl.c:504: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

我不确定此时还能尝试什么。欢迎任何帮助!


你可以使用 strace -e open,stat ... 命令来检查 Python 安装是否使用了你期望的 cacert.pem,或者它是否在其他地方寻找证书。 - Steffen Ullrich
strace 显示它正在使用 cacert.pem 文件。我还通过故意向 cacert.pem 添加一些格式不正确的数据来验证它是否会触发请求失败(确实如此)。 - Linda Gardner
经过进一步的调查,我认为这可能是密码问题。我一直在尝试找出一种方法,在不必修补代码的情况下指定一组受限制的密码,但看起来似乎不可能。 - Linda Gardner
3个回答

5

1

Python2不支持SNI,requests在这方面也没有帮助,参见http://docs.python-requests.org/en/latest/community/faq/。但是,如果没有使用SNI访问,则服务器将返回自签名证书:

$ openssl s_client -connect jordan-cu.org:443 | openssl x509 -text -noout
...
Issuer: C=US, ST=Virginia, L=Herndon, O=Parallels, OU=Parallels Panel, CN=Parallels Panel/emailAddress=info@parallels.com
...
Subject: C=US, ST=Virginia, L=Herndon, O=Parallels, OU=Parallels Panel, CN=Parallels Panel/emailAddress=info@parallels.com

如果使用SNI访问服务器,它会返回由公共CA签名的证书。
$ openssl s_client -connect jordan-cu.org:443 -servername jordan-cu.org | openssl x509 -text -noout
...
Issuer: C=US, O=GeoTrust, Inc., CN=RapidSSL CA
...
Subject: ... CN=*.jordan-cu.org

由于Python2在TLS连接时没有使用SNI,因此您将获得自签名证书,当然无法验证与相匹配,因此会出现错误。

修复方法:升级到支持SNI的Python3。


0

您需要创建一个包含完整证书链的PEM文件。您的证书+中间证书+根证书。


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