证书验证失败:无法获取本地颁发者证书

时间:2018-10-14 17:12:19

标签: python python-3.x ssl openssl

我正在尝试使用python从网络获取数据。我为此导入了urllib.request包,但是在执行时却出现错误:

certificate verify failed: unable to get local issuer certificate (_ssl.c:1045)

当我将URL更改为“ http”时-我能够获取数据。但是,我相信这可以避免检查SSL证书。

因此,我在互联网上进行了检查,找到了一种解决方案: 运行/Applications/Python\ 3.7/Install\ Certificates.command

这解决了我的问题。但是我对SSL之类的东西一无所知。您能否帮助我了解它实际上解决了我的问题。

如果可能,请向我推荐任何有用的资源,以了解有关安全性和证书的信息。我是新来的。

谢谢!

注意:我确实通过链接-openssl, python requests error: "certificate verify failed"

我的问题不同于链接中的问题,因为我想知道在安装certifi软件包或运行Install\ Certificates.command来修复错误时实际发生的情况。我对证券知之甚少。

14 个答案:

答案 0 :(得分:11)

对于仍然想知道如何解决此问题的任何人,我都安装了“ Install Certificates.command

这是我的做法,

Install Certificates.commad location

只需双击该文件等待其安装,就我而言,您就可以开始使用

答案 1 :(得分:6)

我想提供参考。我使用cmd +空格,然后键入Install Certificates.command,然后按Enter。片刻之后,命令行界面弹出,开始安装。

 -- removing any existing file or link
 -- creating symlink to certifi certificate bundle
 -- setting permissions
 -- update complete

最后,它修复了错误。

答案 2 :(得分:3)

此页面是Google在“证书验证失败:无法获取本地发行者证书”方面的热门搜索,因此虽然这不能直接回答原始问题,但下面是针对具有相同症状的问题的解决方案。我在尝试将TLS添加到xmlrpc服务时遇到了这个问题。这需要使用相当低级的<body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="app"> <div class="loading"></div> </div> </body> 类。该错误表明证书丢失。解决方法是在构造ssl.SSLContext对象时做几件事:

首先,在客户端中:

SSLContext

在服务器中,您需要在上下文中安装中间证书:

def get_client():
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    # Load the default certs:
    context.load_default_certs()

    # Optionally, install the intermediate certs.
    # This _should_ be handled by the server, but
    # maybe helpful in some cases.
    # context.load_verify_locations('path/to/ca_bundle.crt')
    return xmlrpc.client.ServerProxy('https://server.yourdomain.com/', context=context)

答案 3 :(得分:3)

在开始时粘贴以下代码:

# paste this at the start of code
import ssl 

try:
    _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    pass
else:
    ssl._create_default_https_context = _create_unverified_https_context

答案 4 :(得分:3)

创建从操作系统证书到 Python 的符号链接对我有用:

ln -s /etc/ssl/* /Library/Frameworks/Python.framework/Versions/3.9/etc/openssl

(我使用的是 macOS,使用 pyenv

答案 5 :(得分:2)

对于那些仍然存在此问题的人:- MacOS上的Python 3.6(还有其他版本吗?)带有自己的OpenSSL私有副本。这意味着Python ssl模块不再将系统中的信任证书用作默认值。要解决此问题,您需要在系统中安装 certifi 软件包。

您可以尝试通过两种方式进行操作:

1)通过PIP:

pip install --upgrade certifi

2)如果不起作用,请尝试运行Python 3.6 for Mac附带的Cerificates.command:

open /Applications/Python\ 3.6/Install\ Certificates.command

一种或另一种方式,您现在应该已经安装了证书,并且Python应该能够通过HTTPS连接而没有任何问题。

希望这会有所帮助。

答案 6 :(得分:1)

对我来说,问题是我在REQUESTS_CA_BUNDLE中设置了.bash_profile

/Users/westonagreene/.bash_profile:
...
export REQUESTS_CA_BUNDLE=/usr/local/etc/openssl/cert.pem
...

一旦我将REQUESTS_CA_BUNDLE设置为空白(即从.bash_profile中删除),requests就会重新工作。

export REQUESTS_CA_BUNDLE=""

该问题仅在通过CLI(命令行界面)执行python requests时出现。如果我运行requests.get(URL, CERT),就可以解决。

Mac OS Catalina(10.15.6)。 3.6.11的Pyenv。 我收到的错误消息:[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)

此答案在其他地方:https://stackoverflow.com/a/64152045/4420657

答案 7 :(得分:1)

Certifi 提供 Mozilla 精心策划的根证书集合,用于验证 SSL 证书的可信度,同时验证 TLS 主机的身份。它是从 Requests 项目中提取的。

pip install certifi

或者运行下面的程序代码:

# install_certifi.py
#
# sample script to install or update a set of default Root Certificates
# for the ssl module.  Uses the certificates provided by the certifi package:
#       https://pypi.python.org/pypi/certifi

import os
import os.path
import ssl
import stat
import subprocess
import sys

STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
             | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
             | stat.S_IROTH |                stat.S_IXOTH )


def main():
    openssl_dir, openssl_cafile = os.path.split(
        ssl.get_default_verify_paths().openssl_cafile)

    print(" -- pip install --upgrade certifi")
    subprocess.check_call([sys.executable,
        "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"])

    import certifi

    # change working directory to the default SSL directory
    os.chdir(openssl_dir)
    relpath_to_certifi_cafile = os.path.relpath(certifi.where())
    print(" -- removing any existing file or link")
    try:
        os.remove(openssl_cafile)
    except FileNotFoundError:
        pass
    print(" -- creating symlink to certifi certificate bundle")
    os.symlink(relpath_to_certifi_cafile, openssl_cafile)
    print(" -- setting permissions")
    os.chmod(openssl_cafile, STAT_0o775)
    print(" -- update complete")

if __name__ == '__main__':
    main()

Brew 没有运行 Mac 的 Python3 包中的 Install Certificates.command。

答案 8 :(得分:0)

我在OSX上遇到了同样的问题,而我的代码在Linux上完全没问题,您给出了问题的答案!

在检查了您指向/Applications/Python 3.7/Install Certificates.command的文件之后,事实证明此命令将{Python}安装的根证书替换为certifi软件包附带的根证书。

certifi是一组根证书。每个SSL证书都依赖一个信任链:您信任一个特定的证书,因为您信任该证书的父级,也信任该父级,等等。在某些时候,没有“父级”证书,而这些证书是“根”证书。对于那些用户,除了捆绑普遍信任的根证书(通常是大型的信任公司,例如“ DigiCert”之类),没有其他解决方案。

例如,您可以在浏览器安全设置中查看根证书(例如,对于Firefox->首选项->隐私和安全性->查看证书->权威)。

回到最初的问题,并且在运行.command文件之前,执行此操作会为我返回干净安装上的空列表:

import os
import ssl                                        
openssl_dir, openssl_cafile = os.path.split(      
    ssl.get_default_verify_paths().openssl_cafile)
# no content in this folder
os.listdir(openssl_dir)
# non existent file
print(os.path.exists(openssl_cafile))

这意味着OSX上没有Python安装的默认证书颁发机构。可能的默认值正是certifi软件包提供的默认值。

之后,您只需创建一个具有正确默认值的SSL上下文,如下所示(certifi.where()给出了证书颁发机构的位置):

import platform
# ...

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = True
ssl_context.load_default_certs()

if platform.system().lower() == 'darwin':
    import certifi
    ssl_context.load_verify_locations(
        cafile=os.path.relpath(certifi.where()),
        capath=None,
        cadata=None)

并从python向url发出请求,如下所示:

import urllib
# previous context
https_handler = urllib.request.HTTPSHandler(context=ssl_context)

opener = urllib.request.build_opener(https_handler)
ret = opener.open(url, timeout=2)

答案 9 :(得分:0)

我在Linux上的conda错误。我的解决方法很简单。

conda install -c conda-forge certifi

由于默认证书似乎有问题,我不得不使用conda伪造。

答案 10 :(得分:0)

这适用于所有操作系统:

HttpResponseMessage responseMessage = new HttpResponseMessage()
{
      Content = new StringContent(JsonConvert.SerializeObject(message)),
      //StatusCode = System.Net.HttpStatusCode.Forbidden
      StatusCode = System.Net.HttpStatusCode.Unauthorized
};

答案 11 :(得分:0)

我最近在连接MongoDB Atlas时遇到了这个问题。我更新到了最新的certifi python软件包,现在可以使用了。

(python 3.8,升级到certifi 2020.4.5.1,以前的证书版本为2019.11.28)

答案 12 :(得分:0)

在我的情况下,此错误的原因是 OPENSSLDIR 设置为不包含实际证书的路径,可能是由某些升级/重新安装引起的。

要验证您是否属于这种情况,请尝试运行:

openssl s_client -CApath /etc/ssl/certs/ -connect some-domain.com:443

如果您删除 -CApath /etc/ssl/certs/ 并获得 20 错误代码,则这可能是原因。您还可以通过运行 openssl version -a 来检查 OPENSSLDIR 的设置。

由于更改 OPENSSLDIR 需要重新编译,我发现最简单的解决方案是在现有路径中创建一个符号链接:ln -s /etc/ssl/certs your-openssldir/certs

答案 13 :(得分:0)

警告:我对证书不是很了解,但我认为这值得尽早检查。

在花费任何时间重新配置您的代码/包/系统之前,请确保您尝试从中下载的服务器没有问题。

我认为该错误可能具有误导性,因为“无法获得本地颁发者证书”使您的本地计算机似乎存在问题,但情况不一定如此。

尝试将您尝试加载的页面更改为可能不错的页面,例如 https://www.google.com,然后查看问题是否仍然存在。此外,请使用 https://www.digicert.com/help/ 中的搜索工具检查给您带来问题的域。

就我而言,DigiCert 的工具告诉我“证书不是由受信任的机构签署的(对照 Mozilla 的根存储进行检查)。”这将解释为什么我似乎安装了根证书但仍然有错误。当我测试使用 HTTPS 加载其他站点时,我没有遇到任何问题。

如果这种情况适用于您,那么我认为您可能有 3 个逻辑选项(按优先顺序排列):1) 如果服务器在您的控制之下,则修复它,2) 在继续使用 HTTPS 的同时禁用证书检查,3)跳过 HTTPS 并转到 HTTP。