45

I installed python 3.6 using

brew install python3

and tried to download a file with six.moves.urllib.request.urlretrieve from an https, but it throws the error

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)

In the Python installation (from .pkg), the README indicates that one needs to run the Install Certificates.command after the installation to

  1. install certifi
  2. symlink the certification path to certify path

to be able to use certificates.

However, in brew install, this file does not exist and it does not seem to be run.

Jorge Leitao
  • 19,085
  • 19
  • 85
  • 121
  • 1
    Executing `/Applications/Python\ 3.6/Install\ Certificates.command` in Terminal fixed this problem with my homebrew python3 installation. – David Douglas Feb 04 '19 at 15:07
  • 1
    Given the popularity of this question, I fielded [a issue to brew](https://github.com/Homebrew/homebrew-core/issues/42198#issue-470594759). – Jorge Leitao Jul 19 '19 at 22:54
  • `export SSL_CERT_DIR=/etc/ssl/certs` worked for me in Mac OS Big Sur. – TrigonaMinima Oct 08 '21 at 20:54
  • This [comment by robo-corg](https://github.com/Homebrew/homebrew-core/issues/42198#issuecomment-564181886) points out the root of the cause. I. e., it is the missing `/opt/homebrew/etc/openssl@1.1/cert.pem`. Now on a mac, you have that file in `/private/etc/ssl/`. So running, `ln -s /private/etc/ssl/cert.pem /opt/homebrew/etc/openssl@1.1/cert.pem` sym links the certificate. And, that resolved the issue. – scribe May 08 '23 at 06:36

6 Answers6

144

It seems that, for some reason, Brew has not run the Install Certificates.command that comes in the Python3 bundle for Mac. The solution to this issue is to run the following script (copied from Install Certificates.command) after brew install python3:

# 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()
Jorge Leitao
  • 19,085
  • 19
  • 85
  • 121
33

My solution for Mac OS X:

1) Upgrade to Python 3.6.5 using the native app Python installer downloaded from the official Python language website https://www.python.org/downloads/

I've found that this installer is taking care of updating the links and symlinks for the new Python a lot better than homebrew.

2) Install a new certificate using "./Install Certificates.command" which is in the refreshed Python 3.6 directory

cd "/Applications/Python 3.6/" sudo "./Install Certificates.command"

Claude COULOMBE
  • 3,434
  • 2
  • 36
  • 39
  • 1
    Thanks! Worked for me, and I'm on 3.8 (changed the command appropriately to the Python 3.8 directory). – user7804781 Dec 12 '19 at 20:47
  • There is no Python folder in my Applications, and I could not locate it on my machine. Any ideas how can I fix the issue for my case? – talha06 Dec 25 '19 at 08:38
  • 2
    @talha06 - In order to know where Python is located on your computer, open the python shell typing `python` or `python3` then `import sys` and `print('\n'.join(sys.path))` Or you can type this command in the terminal `python -c "import sys; print('\n'.join(sys.path))"` – Claude COULOMBE Dec 26 '19 at 05:09
  • Thanks worked for me with tiny change: `$ cd /Applications/Python 3.8/` `$ sudo ./Install\ Certificates.command` Happy Coding :) – Mehady Apr 10 '21 at 13:19
  • After 2 dys of searching. this is what worked for me. Didn't even make it to the approved solution. – insaineyesay Feb 03 '22 at 23:34
24
  • find out default cafile:
python -c 'import ssl; print(ssl.get_default_verify_paths().openssl_cafile)'

/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/etc/ssl/cert.pem

sudo mkdir -p /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/etc/ssl/certs
  • find out ca file of certifi
python -c 'import certifi; print(certifi.where())'

'/usr/local/lib/python3.7/site-packages/certifi/cacert.pem'

  • copy to
sudo cp /usr/local/lib/python3.7/site-packages/certifi/cacert.pem
/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/etc/ssl/certs/cert.pem
Ken
  • 1,141
  • 12
  • 12
  • This did not work for me on a conda python environment, using conda python. I tried copying the certs in three different directories: ssl, ssl/crt, etc/ssl/certs . I have found no solution so far. – user1255933 Nov 23 '20 at 20:00
  • @user1255933 how did you solve this? – user2510479 Sep 16 '22 at 19:49
  • This is the correct answer to this question, awesome way to find the default `cert.pem`! – guychouk Sep 17 '22 at 15:54
  • 1
    Thank you. My work uses Zscaler and I was using a miniconda python install. This gave me the clue I needed to find the offending openssl_cafile. I copied the Zscaler root CA over /Users/xxx/miniconda3/ssl/cert.pem and my problem was solved. – Luke Rohde Sep 27 '22 at 13:10
  • The output from `python -c 'import ssl; print(ssl.get_default_verify_paths().openssl_cafile)'` was a non-existing file for me. I simply copied the file at the path from `python -c 'import certifi; print(certifi.where())'` to the path of the former command. The exact steps in this answer did not work for me. Using RHEL7 – Magnus Dec 13 '22 at 09:02
8

For temporary, following will disable the ssl checking,

import ssl
ssl._create_default_https_context = ssl._create_unverified_context
Tasneem Hyder
  • 170
  • 1
  • 1
  • 7
Mak Insane
  • 137
  • 2
  • 9
5

If you need to make your local root certificate (e.g. local_RootCA.crt) become trusted by python, you can add it into the end of certifi/cacert.pem file:

cat local_RootCA.crt >> `python -c 'import certifi; print(certifi.where())'`

That solution works good for macos brew python 3 installation as well.

rfedorov
  • 689
  • 7
  • 11
  • 2
    After a day of trying different things in this and other SOF conversations, this is what worked for us. Thanks! – Joel Jul 29 '22 at 16:05
0

positionning SSL_CERT_FILE env var to your ca file also works and it is not invasive.

G. Ghez
  • 3,429
  • 2
  • 21
  • 18