certificate verify failed: unable to get local issuer certificate

PythonPython 3.xSslOpenssl

Python Problem Overview


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

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.

So I checked on the internet and found one solution: Run /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.

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 - https://stackoverflow.com/questions/22027418/openssl-python-requests-error-certificate-verify-failed

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

Python Solutions


Solution 1 - Python

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

Here is how I did,

Install Certificates.commad location

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

Solution 2 - Python

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

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 certifi package.

certifi is 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").

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

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

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(os.path.join(openssl_dir, 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 certifi package.

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):

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 url from python like this:

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)

Solution 3 - Python

Creating a symlink from OS certificates to Python worked for me:

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

(I'm on macOS, using pyenv)

Solution 4 - Python

This worked in all OS:

import ssl
import certifi
from urllib.request import urlopen

request = "https://example.com"
urlopen(request, context=ssl.create_default_context(cafile=certifi.where()))

Solution 5 - Python

For those who this problem persists: - Python 3.6 (some other versions too?) on MacOS comes with its own private copy of OpenSSL. That means the trust certificates in the system are no longer used as defaults by the Python ssl module. To fix that, you need to install a certifi package in your system.

You may try to do it in two ways:

  1. Via PIP:

    pip install --upgrade certifi

  2. If it doesn't work, try to run a Cerificates.command that comes bundled with Python 3.6 for Mac:

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

One way or another, you should now have certificates installed, and Python should be able to connect via HTTPS without any issues.

Hope this helped.

Solution 6 - Python

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.

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

Finally, it fixes the errors.

Solution 7 - Python

Paste the following code at the start:

# 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

Solution 8 - Python

The cause for this error in my case was that OPENSSLDIR was set to a path which did not contain the actual certificates, possibly caused by some upgrading / reinstallation.

To verify this if this might be the case for you, try running:

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

If you remove the -CApath /etc/ssl/certs/ and get a 20 error code, then this is the likely cause. You can also check what the OPENSSLDIR is set to by running openssl version -a.

Since changing the OPENSSLDIR requires re-compilation, I found the easiest solution to be just creating a symlink in the existing path: ln -s /etc/ssl/certs your-openssldir/certs

Solution 9 - Python

Certifi provides Mozilla’s carefully curated collection of Root Certificates for validating the trustworthiness of SSL certificates while verifying the identity of TLS hosts. It has been extracted from the Requests project.

pip install certifi

Or running the program code below:

# 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 has not run the Install Certificates.command that comes in the Python3 bundle for Mac.

Solution 10 - Python

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

conda install -c conda-forge certifi

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

Solution 11 - Python

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.SSLContext class. The error indicates that a certificate is missing. The fix was to do several things when constructing SSLContext objects:

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

Solution 12 - Python

Caveat: I am not super knowledgeable about certificates, but I think this is worth checking early.

Before spending any time reconfiguring your code/packages/system, make sure it isn't an issue with the server you are trying to download from.

I think the error can be misleading because "unable to get local issuer certificate" makes it seems like it's a problem with your local machine, but that may not necessarily be the case.

Try changing the page you are trying to load to something that is probably good, like https://www.google.com and see if the issue persists. Additionally, check the domain that's giving you problems against the search tool at https://www.digicert.com/help/.

In my case, DigiCert's tool told me that "The certificate is not signed by a trusted authority (checking against Mozilla's root store)." That would explain why I seemed to have the root certificates installed but still had the error. When I tested loading a different site with HTTPS, I had no issues.

If this case applies to you, then I think you probably have 3 logical options (in order of preference): 1) fix the server if it's under your control, 2) disable certificate checking while continuing to use HTTPS, 3) skip HTTPS and go to HTTP.

Solution 13 - Python

For me the problem was that I was setting REQUESTS_CA_BUNDLE in my .bash_profile

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

Once I set REQUESTS_CA_BUNDLE to blank (i.e. removed from .bash_profile), requests worked again.

export REQUESTS_CA_BUNDLE=""

The problem only exhibited when executing python requests via a CLI (Command Line Interface). If I ran requests.get(URL, CERT) it resolved just fine.

Mac OS Catalina (10.15.6). Pyenv of 3.6.11. Error message I was getting: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)

This answer elsewhere: https://stackoverflow.com/a/64152045/4420657

Solution 14 - Python

As the question don't have the tag [macos] I'm posting a solution for the same problem under ubuntu :

sudo apt install ca-certificates
sudo update-ca-certificates --fresh
export SSL_CERT_DIR=/etc/ssl/certs

Solution come form Zomatree on Github.

Solution 15 - Python

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

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBiswajit PaulView Question on Stackoverflow
Solution 1 - PythonAyyoubView Answer on Stackoverflow
Solution 2 - PythonRaffiView Answer on Stackoverflow
Solution 3 - PythonAlexaView Answer on Stackoverflow
Solution 4 - PythonKenView Answer on Stackoverflow
Solution 5 - PythonYash GoenkaView Answer on Stackoverflow
Solution 6 - PythonwangView Answer on Stackoverflow
Solution 7 - PythonVarun RajkumarView Answer on Stackoverflow
Solution 8 - PythonMaxView Answer on Stackoverflow
Solution 9 - PythonMilovan TomaševićView Answer on Stackoverflow
Solution 10 - PythonnetskinkView Answer on Stackoverflow
Solution 11 - PythonLucas WimanView Answer on Stackoverflow
Solution 12 - PythonStephenView Answer on Stackoverflow
Solution 13 - PythonWeston GreeneView Answer on Stackoverflow
Solution 14 - PythonGaligatorView Answer on Stackoverflow
Solution 15 - PythonJoe WalkerView Answer on Stackoverflow