certificate verify failed: unable to get local issuer certificate

231,863

Solution 1

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

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

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

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

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.

Share:
231,863
biswajit
Author by

biswajit

Hello, there!

Updated on July 08, 2022

Comments

  • biswajit
    biswajit almost 2 years

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

    • stovfl
      stovfl over 5 years
    • biswajit
      biswajit over 5 years
      @stovfl - I read from the link provided you. Just to clear (I don't know SSL and the likes): 1. Is OpenSSL library native to the OS I am using or Python uses its own? 2. When my code is trying get data from a particular website, it checks for the website's certificate in the OpenSSL root and as it doesn't trust it by default, it throws me the error. Am I right? 3. If so, then what happens when I run install Certificates.command?
    • stovfl
      stovfl over 5 years
      1. Yes, pyopenssl is a wrapper to this. 2. Didn't know if requests uses pyopenssl at all, but faild to verify the certificate which results throwing the error. 3. Didn't know Certificats.command, assume update/install the needed Certificates, requests needs.
    • Maged
      Maged over 3 years
  • Pynchia
    Pynchia over 4 years
    How exactly do you install it? With brew? Please explain
  • bli
    bli over 4 years
    You wrote: os.path.exists(openssl_cafile). Shouldn't this rather be os.path.exists(os.path.join(openssl_dir, openssl_cafile))?
  • numbermaniac
    numbermaniac about 4 years
    It's a .command file, which means you should just be able to double-click it and it will run in the Terminal.
  • dnovai
    dnovai about 4 years
    It was very useful for me. I had same issue (macOS high Sierra + Python 3.7). Thank you!
  • Joseph Astrahan
    Joseph Astrahan about 4 years
    but where do you get this file?
  • Raffi
    Raffi about 4 years
    @JosephAstrahan it is the standard python installation package from www.python.org . Also this is the official python release (I usually install this instead of the one from homebrew)
  • Alex Pavtoulov
    Alex Pavtoulov about 4 years
    I had permission errors so I had to first run sudo pip install --upgrade certifi in the terminal, and then double-click Install Certificates.command
  • jpaugh
    jpaugh almost 4 years
    Am I correct in assuming, this avoids checking the SSL certrificate's validity?
  • Juha Palomäki
    Juha Palomäki over 3 years
    This skips the certificate validation.
  • Abdalhady Alsayad
    Abdalhady Alsayad over 3 years
    It was very useful
  • Dejan Dozet
    Dejan Dozet almost 3 years
    This is the best because of its simplicity!
  • Stephen
    Stephen almost 3 years
    @bli I edited the answer to make that fix
  • Colin 't Hart
    Colin 't Hart over 2 years
    Don't do this! "My house key doesn't work! Just leave the door unlocked all the time."
  • wiwinut
    wiwinut over 2 years
    After so many attempts and suggestions from various sources, #2 worked for me! Thanks so much!
  • SuperFunkyMonkey
    SuperFunkyMonkey over 2 years
    With brew, this worked for me: security find-certificate -a -p > ~/all_mac_certs.pem; export SSL_CERT_FILE=~/all_mac_certs.pem; export REQUESTS_CA_BUNDLE=~/all_mac_certs.pem
  • Tom el Safadi
    Tom el Safadi over 2 years
    Didn't think this was gonna work but it did. Thanks!
  • Raleigh L.
    Raleigh L. over 2 years
    What is request defined as?
  • Guss
    Guss over 2 years
    I'm using Python 3.9.3 through brew, and for me the command was ln -s /etc/ssl /usr/local/etc/[email protected]
  • Mxngls
    Mxngls over 2 years
    I still don't understand this. Until a couple of days before my program worked just fine. Then suddenly out of the blue I get this error message. I am still not sure if the problem lies with myself or the site I am trying to reach. I somehow can get a response when sending a GET request to Google, but not to the (unrelated URLs) of two sites I try to reach... this is driving me nuts
  • Alexandre Strube
    Alexandre Strube about 2 years
    I get error_20 with one version of openssl in one machine, but not the others. The -CApath thing is irrelevant
  • Max
    Max about 2 years
    Could be that the two versions of openssl each look in different CA paths?
  • Alexandre Strube
    Alexandre Strube about 2 years
    Turns out the system’s OpenSSL certs were old, and installing OpenSSL from source doesn’t bring new certs. An os upgrade solved it (it was a supercomputer with centos 7 on all nodes)
  • user3325025
    user3325025 about 2 years
    this worked for me, tx!
  • Jay Slupesky
    Jay Slupesky about 2 years
    Worked for me. Using Python 3.9.
  • Rakibul Islam
    Rakibul Islam almost 2 years
    It works on mac air M1.