Windows: Python SSL证书验证失败

27
我已经安装了Anaconda,并且在尝试通过Jupyter Notebooks进行API调用时遇到了SSL问题。
import requests
import certifi

r = requests.get('https://github.com/')
print(r)

这首先导致了一个SSL连接错误。经过广泛的搜索和我们IT部门的帮助,我终于解决了这个问题。解决方案是将公司的根证书添加到certifi证书存储中。
现在对于其他请求,不幸的是我仍然遇到相同的问题。例如,使用google2pandas包调用Google Analytics API的示例代码:
from google2pandas import *

query = {
    'reportRequests': [{
        'viewId' : 37616054,

        'dateRanges': [{
            'startDate' : '8daysAgo',
            'endDate'   : 'today'}],

        'dimensions' : [
            {'name' : 'ga:date'}, 
            {'name' : 'ga:pagePath'},
            {'name' : 'ga:browser'}],

        'metrics'   : [
            {'expression' : 'ga:pageviews'}],

        'dimensionFilterClauses' : [{
            'operator' : 'AND',
            'filters'  : [
                {'dimensionName' : 'ga:browser',
                 'operator' : 'REGEXP',
                 'expressions' : ['Firefox']},

                {'dimensionName' : 'ga:pagePath',
                 'operator' : 'REGEXP',
                 'expressions' : ['iPhone']}]
        }]
    }]
}

# Assume we have placed our client_secrets_v4.json file in the current
# working directory.

conn = GoogleAnalyticsQueryV4(secrets='Analytics.json')
df = conn.execute_query(query)

这里我仍然遇到了之前简单调用时的SSL错误:

C:\ProgramData\Anaconda3\lib\ssl.py中的_create方法,参数(sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, context, session) 848 # 非阻塞 849 raise ValueError("对于非阻塞套接字,不应指定do_handshake_on_connect") --> 850 self.do_handshake() 851 except (OSError, ValueError): 852 self.close()

C:\ProgramData\Anaconda3\lib\ssl.py中的do_handshake方法,参数(block)
1106 if timeout == 0.0 and block: 1107
self.settimeout(None) -> 1108 self._sslobj.do_handshake() 1109 finally: 1110 self.settimeout(timeout)

SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] 证书验证失败:无法获取本地颁发者证书(_ssl.c:1045)

我相信有另一个正在使用的库,它不依赖于certifi。但是我不知道在哪里以及如何添加我的根证书,以便所有的iPython请求都能正常工作。
欢迎提供任何想法。

当您调用GoogleAnalytics API时,作为SSL握手的一部分,它将呈现其证书。您需要获取证书颁发者的证书以使握手通过。因此,您需要获取它并将其添加到您的存储中。 - Jay
嗨@Jay,感谢您的回复。不幸的是,我并没有完全理解这个答案。我确实将公司根证书添加到了certifi中。我需要将它添加到第二个存储库中才能进行握手验证吗?我对SSL的知识不是很多。在我们公司网络之外,Python库发出的相同请求没有任何问题。 - Andii
6个回答

27

我花了几天时间思考如何解决这个问题。最终我在requests库使用的配置文件中添加了我的公司的CA证书。你可以通过以下方式检查此文件的路径:

import requests as r
print(r.certs.where())

打印Python使用的cacert.pem文件路径,编辑它并在其底部附加CA证书。


你能告诉我证书文件在哪里吗?我已经通过 print(r.certs.where()) 获取了 .pem 文件,并使用 r = requests.get("https://mycompany.com/home", cert="C:/.../site-packages/certifi/cacert.pem")。但仍然遇到了与上述相同的错误。 - dhinar1991
1
打开你找到的.pem文件,使用文本编辑器滚动到文件底部,然后将你的证书粘贴在那里。 - m33n
这个可行,非常感谢!看起来也可以覆盖certifi.core.where函数,使用你公司的证书文件,避免编辑(我猜这样更适合分发解决方案)。在这里查看详细信息:https://dev59.com/TFYO5IYBdhLWcg3wPvRv#47699138 - user110954

23
pip install pip-system-certs

根据评论所述,已过时:
一个可能的解决方案是指示Python使用您的Windows证书存储而不是certifi软件包中的内置存储。您可以通过安装python-certifi-win32来实现这一点。
pip install python-certifi-win32

Python在使用与您的浏览器相同的证书。

3
请注意 >>> 这可能已经过时,如此指示:https://dev59.com/fFUL5IYBdhLWcg3wYXN8#57053415 - nate
2
我在Windows上尝试了这个解决方案,使用Python 3.10和Scoop,结果破坏了我的pip安装。现在当我使用pip install时,会出现TypeError: can only concatenate str (not "method") to str的错误。请注意此解决方案。 - Asker
1
对我之前评论的详细说明:我刚刚发现pip中的任何命令现在都会引发上述错误。因此,运行这个命令破坏了我的pip - Asker
3
显然,pip-system-certs(由同一作者)是替换python-certifi-win32的解决方案,但我还没有尝试过。 - NeilG
这对我在 Python v3.9 和 Windows 10 上有效。 - 108

5
由于安全原因,上述解决方案并不是很适合。这个问题的更好解决方案是:
  • 访问https://letsencrypt.org/certificates/,下载 "ISRG Root X1"、" ISRG Root X2" 和 "Let’s Encrypt R3"(.der 文件)。
  • 将这三个证书导入本地受信任的根证书颁发机构。请参考此指南进行操作。
  • 有可能需要重新启动您的系统。

2
你可以对 ssl.SSLSocket__init__ 方法进行猴子补丁,强制将 cert_reqs=CERT_NONE 参数设置为忽略 SSL 证书验证。
在脚本开头添加以下内容:
import ssl
orig_sslsocket_init = ssl.SSLSocket.__init__
ssl.SSLSocket.__init__ = lambda *args, cert_reqs=ssl.CERT_NONE, **kwargs: orig_sslsocket_init(*args, cert_reqs=ssl.CERT_NONE, **kwargs)

1
谢谢,但这意味着不再通过SSL进行安全保护了,对吗? - Andii
正确。使用此选项,连接仍将通过SSL加密,但证书将不会被验证。如果您无法安装适当的根证书,请使用此方法。 - blhsing

1
在每个“request.SOMETHING”中添加:
verify=False 

作为一个参数
例如

 re=requests.get(str(url)+"/"+str(f), headers=headers)

变成


 re=requests.get(str(url)+"/"+str(f), headers=headers, verify=False)


这个对我有用! - undefined

0

我已经取得了进展。问题出在httplib2这个包上。它有自己的cacerts.txt文件,现在我已经将根证书添加进去了。

祝好,

安德烈亚斯


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