Python 2.7 Python请求不处理仅来自一台计算机的丢失的中间证书

Python 2.7 Python请求不处理仅来自一台计算机的丢失的中间证书,python-2.7,ssl-certificate,python-requests,Python 2.7,Ssl Certificate,Python Requests,我正在使用一个运行CentOS(Linux)的盒子,在尝试访问特定子域进行工作时遇到以下错误: Traceback (most recent call last): ... # My code, relevant call is requests.get(url) File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 60, in get return request('get', url, **k

我正在使用一个运行CentOS(Linux)的盒子,在尝试访问特定子域进行工作时遇到以下错误:

Traceback (most recent call last):
  ... # My code, relevant call is requests.get(url)
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 60, in get
    return request('get', url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 49, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-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
根据,子域“没有发送所需的中间证书”(这是DigiCert发现的唯一问题)。然而,当我在Mac笔记本电脑上运行它时,我的代码可以毫无问题地处理这个问题,Chrome和Safari也是如此。我在笔记本电脑和linux机器上都运行Python 2.7.5。我在linux机器上运行请求1.2.0,在笔记本电脑上运行请求2.2.1,但我将两者升级到了2.4.3,它们仍然没有相同的行为

也可能相关-同一证书正在用于发送中间证书的其他子域,我的笔记本电脑和linux box都没有任何问题,因此我的笔记本电脑不应该有linux box没有的根CA


有人知道为什么它不能在我的linux机器上工作,以及我如何修复它吗?

我仍然不明白为什么它在一个地方工作,而不是在另一个地方,但我确实找到了一种可以接受的解决方法,它比关闭证书验证要好得多

根据,如果安装在系统上,则将使用。所以我安装了certifi

sudo pip install certifi
然后修改了它使用的.pem文件。您可以使用
certifi.where()
找到文件位置:

我将中间密钥添加到该.pem文件中,现在可以使用了。仅供参考,.pem文件希望证书显示为

-----BEGIN CERTIFICATE-----
<certificate here>
-----END CERTIFICATE-----
----开始证书-----
-----结束证书-----
警告:这不是真正的解决方案,只是一种变通方法。从安全角度来看,告诉系统信任证书可能是危险的。如果您不了解证书,则不要使用此解决方法,除非您的另一个选项是完全关闭证书验证

此外,从请求文档中:

为了安全起见,我们建议经常升级certifi


我假设当您升级certifi时,您必须重做对文件所做的任何更改。我还没有充分研究它,以了解如何进行更改,使其在certifi更新时不会被覆盖。

我花了一天的时间完全理解并解决了这个问题,因此我想与大家分享我的发现会很好:-)!以下是我的结果:

SSL服务器配置中的一个常见缺陷是提供不完整的证书链,通常会忽略中间证书。例如,我正在使用的一个站点在服务器的响应中没有包含常见的DigiCert“中间”证书“DigiCert TLS RSA SHA256 2020 CA1”

由于此配置缺陷很常见,大多数(但不是所有)现代浏览器都会实施一种称为“AIA抓取”的技术来动态修复此缺陷(参见示例)

Python的SSL支持不支持AIA抓取,依赖于来自服务器的完整证书链;否则它会抛出一个异常,就像这样

SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1124)')))
目前正在讨论是否应将AIA获取添加到Python中,例如,在以下线程中:

我的印象是,在可预见的未来,这仍然是一个悬而未决的问题。

现在,我们如何解决这个问题?

  • 如果尚未安装certifi,请安装或更新它
  • pip安装认证fi

    pip安装认证——升级

    许多(但不是所有)Python模块都可以使用
    certifi
    中的证书,而
    certifi
    Mozilla CA证书计划中获取这些证书。基本上,certifi从Mozilla站点创建一个干净的*.pem文件,并提供一个轻量级Python接口来访问该文件

  • 以PEM语法的文件形式下载丢失的证书,例如从可信浏览器下载或从可信浏览器下载

  • 使用找到certifi*.PEM证书文件

     import certifi
     print(certifi.where())
    
  • 注意:我建议首先激活虚拟环境(例如
    conda activate
    您要使用证书。文件路径会有所不同。如果将此应用于基本环境,任何潜在的有缺陷证书都将使所有代码的整个SSL机制处于危险之中

    示例: /Users/username/anaconda3/envs/environment\u name/lib/python3.8/site-packages/certifi/cacert.pem

    使用一个简单的文本编辑器,打开该文件,并在头的后面插入缺少的证书,如下所示

    ## ## Bundle of CA Root Certificates ## ... -----BEGIN CERTIFICATE----- +I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGB Afr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6J ---> This is the additional certificate. +I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGB Afr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6J -----END CERTIFICATE----- ## ##CA根证书束 ## ... -----开始证书----- +I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGB Afr5yjK7tI4nhyfFK3TUqNaX3sNk+CRU6J --->这是附加证书。 +I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGB Afr5yjK7tI4nhyfFK3TUqNaX3sNk+CRU6J -----结束证书----- 包括开始和结束标记非常重要。

    保存该文件,您应该已完成所有设置

    您可以通过以下几行测试它是否有效:

    # Python 3 import urllib.request import certifi import requests URL = 'https://www.the_url_that_caused_the_trouble.org' print('Trying urllib.request.urlopen().') r = urllib.request.urlopen(URL) print(f'urllib.request.urlopen\n================\n {r.read()[:80]}') print('Trying requests.get().') r = requests.get(URL) print(f'requests.get()\n================\n {r.text[:80]}') #Python 3 导入urllib.request导入certifi导入请求 URL='1〕https://www.the_url_that_caused_the_trouble.org' 打印('Trying urllib.request.urlopen() r=urllib.request.urlopen(URL) 打印(f'urllib.request.urlopen\n=========================\n{r.read()[:80]}') 打印('Trying requests.get()'。) r=请求。获取(URL) 打印(f'requests.get()\n====================\n{r.text[:80]}') 注意:通用SSL证书(例如openssl)可能位于其他位置,因此您可能需要在那里尝试相同的方法:

    /Users/username/anaconda3/envs/environment\u name/ssl

    注意事项:

  • 当您更新certifi或创建新的虚拟环境时,更改可能会丢失,但 # Python 3 import urllib.request import certifi import requests URL = 'https://www.the_url_that_caused_the_trouble.org' print('Trying urllib.request.urlopen().') r = urllib.request.urlopen(URL) print(f'urllib.request.urlopen\n================\n {r.read()[:80]}') print('Trying requests.get().') r = requests.get(URL) print(f'requests.get()\n================\n {r.text[:80]}')