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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/52805115/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 20:11:57  来源:igfitidea点击:

certificate verify failed: unable to get local issuer certificate

pythonpython-3.xsslopenssl

提问by Biswajit Paul

I am trying to get data from the web using python. I imported urllib.request package for it but while executing, I get error:

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

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

When I changed the URL to 'http' - I am able to get data. But, I believe, this avoids checking SSL certificate.

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

So I checked on the internet and found one solution: Run /Applications/Python\ 3.7/Install\ Certificates.command

于是上网查了一下,找到了一个解决办法:运行 /Applications/Python\ 3.7/Install\ Certificates.command

This solved my problem. But I have no knowledge on SSL and the likes. Can you help me understand what it actually did to solve my issue.

这解决了我的问题。但我对 SSL 之类的知识一无所知。你能帮我理解它实际上做了什么来解决我的问题。

If possible, please recommend me any good resource to learn about the security and certificates. I am new to this.

如果可能,请向我推荐任何好的资源来了解安全性和证书。我是新来的。

Thanks!

谢谢!

Note: I did go through the link - openssl, python requests error: "certificate verify failed"

注意:我确实通过了链接 - openssl,python 请求错误:“证书验证失败”

My question differs from the one in link because, I want to know what actually happens when I install certifipackage or run Install\ Certificates.commandto fix the error. I have a poor understanding of securities.

我的问题与链接中的问题不同,因为我想知道当我安装certifi包或运行Install\ Certificates.command以修复错误时实际发生了什么。我对证券的理解很差。

回答by Ayoub

For anyone who still wonders on how to fix this, i got mine by installing the "Install Certificates.command"

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

Here is how I did,

这是我的做法,

Install Certificates.commad location

安装 Certificates.commad 位置

Just double click on that file wait for it to install and in my case, you will be ready to go

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

回答by Raffi

I hit the same issue on OSX, while my code was totally fine on Linux, and you gave the answer in your question!

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

After inspecting the file you pointed to /Applications/Python 3.7/Install Certificates.command, it turned out that what this command replaces the root certificates of the default Python installation with the ones shipped through the certifipackage.

检查您指向的文件/Applications/Python 3.7/Install Certificates.command后,发现该命令将默认 Python 安装的根证书替换为通过certifi包提供的根证书。

certifiis a set of root certificates. Each SSL certificate relies a chain of trust: you trust one specific certificate because you trust the parent of that certificate, for which you trust the parent, etc. At some point, there is no "parent" and those are "root" certificates. For those, there is no other solution than bundling commonly trusted root certificates (usually big trust companies like eg. "DigiCert").

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

You can for instance see the root certificates in your browser security settings (for instance for Firefox->Preference->Privacy and security->view certificates->Authorities).

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

Coming back to the initial problem, and prior to running the .commandfile, executing this returns for me an empty list on a clean installation:

回到最初的问题,在运行.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))

This means that there is no default certificate authority for the Python installation on OSX. A possible default is exactly the one provided by the certifipackage.

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

After that, you just can create an SSL context that has the proper default as the following (certifi.where()gives the location of a certificate authority):

之后,您只需创建一个具有正确默认值的 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)

and make request to an urlfrom python like this:

url像这样向来自 python 的请求发出请求:

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)

回答by wang

I would like to provide a reference. I use cmd + space, then type Install Certificates.command, and then press Enter. After a short while, the command line interface pops up to start the installation.

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

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

Finally, it fixes the errors.

最后,它修复了错误。

回答by RecursivelyIronic

This page is the top google hit for "certificate verify failed: unable to get local issuer certificate", so while this doesn't directly answer the original question, below is a fix for a problem with the same symptom. I ran into this while trying to add TLS to an xmlrpc service. This requires use of the fairly low-level ssl.SSLContextclass. The error indicates that a certificate is missing. The fix was to do several things when constructing SSLContextobjects:

此页面是“证书验证失败:无法获得本地颁发者证书”的谷歌热门搜索,因此虽然这不能直接回答原始问题,但下面是对具有相同症状的问题的修复。我在尝试将 TLS 添加到 xmlrpc 服务时遇到了这个问题。这需要使用相当低级的ssl.SSLContext类。该错误表明缺少证书。修复是在构造SSLContext对象时做几件事:

First, in the client:

首先,在客户端:

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)

In the server, you need to install the intermediate certs in the context:

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

class SecureXMLRPCServer(socketserver.TCPServer, 
        xmlrpc.server.SimpleXMLRPCDispatcher):
    # https://gist.github.com/monstermunchkin/1100226
    allow_reuse_address = True

    def __init__(self, addr, certfile, keyfile=None,
            ca_bundle=None,
            requestHandler=xmlrpc.server.SimpleXMLRPCRequestHandler,
            logRequests=True, allow_none=False, encoding=None, 
            bind_and_activate=True, ssl_version=ssl.PROTOCOL_TLSv1_2):
        self.logRequests = logRequests

        # create an SSL context
        self.context = ssl.SSLContext(ssl_version)
        self.context.load_default_certs()

        # The server is the correct place to load the intermediate CA certificates:
        self.context.load_verify_locations(ca_bundle)
        self.context.load_cert_chain(certfile=certfile, keyfile=keyfile)

        xmlrpc.server.SimpleXMLRPCDispatcher.__init__(self, allow_none, 
                encoding)
        # call TCPServer constructor
        socketserver.TCPServer.__init__(self, addr, requestHandler, 
                bind_and_activate)

        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
            flags |= fcntl.FD_CLOEXEC
            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)

    def get_request(self):
        newsocket, fromaddr = self.socket.accept()
        # create an server-side SSL socket
        sslsocket = self.context.wrap_socket(newsocket, server_side=True)
        return sslsocket, fromaddr

回答by netskink

I had the error with conda on linux. My solution was simple.

我在 linux 上使用 conda 时出错。我的解决方案很简单。

conda install -c conda-forge certifi

I had to use the conda forge since the default certifi appears to have problems.

我不得不使用 conda forge,因为默认证书似乎有问题。

回答by Ken

This worked in all OS:

这适用于所有操作系统:

import ssl
import certifi

urlopen(request, context=ssl.create_default_context(cafile=certifi.where()))

回答by Joe Walker

I recently had this issue while connecting to MongoDB Atlas. I updated to the latest certifipython package and it works now.

我最近在连接到 MongoDB Atlas 时遇到了这个问题。我更新到最新的certifipython 包,它现在可以工作了。

(python 3.8, upgraded to certifi 2020.4.5.1, previously certifi version 2019.11.28)

(python 3.8,升级到certifi 2020.4.5.1,之前certifi版本2019.11.28)