85

I need to use custom root certificates on the company intranet and loading them in the Mac OS TrustStore (KeyChain) does solve the problem for all browsers and GUI apps.

It seems that it works even with the version of curl that ships with Mac OS X but it doesn't work with python, even the version that ships with Mac OS 10.12 Sierra (Python 2.7.10)

Still, it seems that I would be hit by:

urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)>

How can I solve this?

Because I encounter this issue in lots and lots of Python tools. I would really appreciate if I find a way to avoid it without having to patch them.

Providing the custom CA certificate myself is not an option because I cannot patch tens of Python tools that I use.

Most of the tools are using the requests library but, there are a few that are using the native ssl support in Python directly.

DevThiman
  • 920
  • 1
  • 9
  • 24
sorin
  • 161,544
  • 178
  • 535
  • 806
  • 1
    Relevant to: https://stackoverflow.com/q/27835619/1959808 – 0 _ Mar 23 '18 at 00:48
  • Why do we need these certificates in the first place? Might that not open you up to security vulnerabilities? – JGFMK Sep 01 '20 at 18:57

10 Answers10

89

This is also a problem in Python 3.6 with MacOS Sierrra. I know your use case is different. But I stumbled upon this thread while investigating this problem. So if anyone is also having this article is worth checking out:

http://www.cdotson.com/2017/01/sslerror-with-python-3-6-x-on-macos-sierra/

In a nutshell: Python 3.6 does not rely on MacOS' openSSL anymore. It comes with its own openSSL bundled and doesn't have access on MacOS' root certificates.

You have two options:

Run an install command shipped with Python 3.6

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

or

Install the certifi package with

pip install certifi

I chose the first option and it worked.

Rodrigue
  • 3,617
  • 2
  • 37
  • 49
j7nn7k
  • 17,995
  • 19
  • 78
  • 88
  • There is also the option to get python from homebrew, which does it for you automatically. – Giacomo Lacava Jun 02 '17 at 06:46
  • The second option is not enough; to create a symlink in the OpenSSL directory is also needed, and that is what the command in the first option does. See [this issue](https://bugs.python.org/issue28150) for details. – nekketsuuu Dec 16 '18 at 03:46
  • Awesome! This also helped for an ansible issue I was having: https://github.com/ansible/ansible/issues/50777 – lifeofguenter Jan 10 '19 at 18:25
  • Neither works for me because @nekketsuuu says why the second one can't work and the first one won't work because there is no sort of Python direction within Applications for me, regardless of if you try 3.6 in folder name or any other version number – demongolem Dec 04 '19 at 17:52
  • 23
    This solution STOPPED working in OSX Catalina with Python 3.7. There is no /Applications/Python 3.7 directory and the Install Certificates command is not present. Installing certification also does NOT solve this issue anymore. #STUCK!! gggrrrrr.... Has anyone solved this on Catalina? – RandallShanePhD Jan 15 '20 at 21:44
  • 2
    This works. If you have custom CA certs, add them to /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/certifi/cacert.pem then re-run /Applications/Python\ 3.7/Install\ Certificates.command – Andy Fraley Mar 25 '20 at 16:29
  • 3
    I think this solution good but not enough for the people using pyenv or direct build of python. After installing `certifi`, we need to follow https://stackoverflow.com/a/57795811/7412781 – Andrew Chong Oct 09 '20 at 11:11
  • (Mostly so I don't forget) I managed to get the second option to work by first copying my self-signed certificates to the end of the `certifi` certificate store that is located at `certifi.where()` (MacOS Catalina, Python 3.9 Homebrew install). – D. Woods May 10 '22 at 16:44
81

Run this to set the appropriate variables. This is a combination of the answers that have already been given here. Put it in your ~/.bash_profile to make it permanent.

CERT_PATH=$(python -m certifi)
export SSL_CERT_FILE=${CERT_PATH}
export REQUESTS_CA_BUNDLE=${CERT_PATH}
Hitesh Patel
  • 920
  • 6
  • 4
  • 4
    Wonderful! This answer was most relevant for fixing my issue within a Python 3.8 virtualenv setup, within which Jupyter notebook ran into the certificate error. I sourced the ".bash_profile" within the env. And that was it. – Shanti Jul 26 '20 at 19:05
  • @Shanti it seems you had solved the problem I am struggeling with … Please take a quick look at my question here: https://stackoverflow.com/questions/64590535/how-to-disable-ssl-verification-on-pipenv-install-package?noredirect=1#comment114209320_64590535 – Madamadam Oct 29 '20 at 21:17
  • 10
    Didn't work for me. Python 3.9.2 installed via `brew` on Mac OS Big Sur 11.2.3 – Marcello Romani Mar 18 '21 at 16:32
  • @MarcelloRomani I use Mac OS Big Sur 11.2.3 and It was successful. – Justo Apr 02 '21 at 01:12
  • Thanks, worked for me! I am running Python 3.8.14 on my Mac installed from source. – progfan Oct 03 '22 at 17:16
  • Thanks @hitesh-patel this worked for me (macOS 12.6 Monterey + pyenv virtualenv Python 3.9.14), but how to make this work for all installed Python versions + imported self-signed certificates? – David Lukac Nov 10 '22 at 22:27
  • Worked for me with Python 3.10 from MacPorts. Thanks! – jamix Apr 28 '23 at 18:11
22

Mac brew install python env.

$ python3
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 16:52:21) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import certifi
>>> certifi.where()
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/certifi/cacert.pem'
>>> 

Or from the command line:

$ python -m certifi

then need link cacert.pem as cert.pem

$ ln -s /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/certifi/cacert.pem cert.pem
$ pwd
/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl

rehash

then work fine.

luckrill
  • 243
  • 2
  • 7
18

This was intended as an edit to an existing question, but since the queue was already full, posting as a separate answer.

Tested on MacOS 12.3.1 with python 3.10 installed with MacPorts.

If you prefer to trust root CA according to your OS, export them from System Roots keychain into a single file:

security export -t certs -f pemseq -k /System/Library/Keychains/SystemRootCertificates.keychain -o bundleCA.pem

If, additionally, you want to trust some internal self-signed CAs, export them, too. They are probably stored under the System keychain:

security export -t certs -f pemseq -k /Library/Keychains/System.keychain -o selfSignedCAbundle.pem

Merge the two files:

cat bundleCA.pem selfSignedCAbundle.pem >> allCAbundle.pem

Export as bash variable

export REQUESTS_CA_BUNDLE=/path/to/allCAbundle.pem

Consider adding the last code snippet to your .bash_profile

Note that REQUESTS_CA_BUNDLE works only for a single file, and does not for a directory.

badbishop
  • 1,281
  • 2
  • 18
  • 38
16

If you put the additional certificates in a PEM bundle file you can use these two environment variables to overwrite the default cert stores used by Python openssl and requests.

SSL_CERT_FILE=/System/Library/OpenSSL/cert.pem
REQUESTS_CA_BUNDLE=/System/Library/OpenSSL/cert.pem

Please note that this file does not exist, you need to build it yourself.

sorin
  • 161,544
  • 178
  • 535
  • 806
7

I see a lot of answers out there recommend to turn off certificate validation or to use certifi.where. While turning off SSL is obvious risk. certifi.where is also a risk, mainly if you intend to make this code a production code that will run in a customer env.

PEP describing why it is wrong. ssl.create_default_context is well integrate with linux and windows trustStore. the problem is, as in your case with mac. I solve this by loading the certificates using the integrated security commandline tool

def create_macos_ssl_context():
    import subprocess
    import ssl
    import tempfile
    ctx = ssl.create_default_context()
    macos_ca_certs = subprocess.run(["security", "find-certificate", "-a", "-p",
                                     "/System/Library/Keychains/SystemRootCertificates.keychain"],
                                    stdout=subprocess.PIPE).stdout

    with tempfile.NamedTemporaryFile('w+b') as tmp_file:
        tmp_file.write(macos_ca_certs)
        ctx.load_verify_locations(tmp_file.name)
    print(ctx.get_ca_certs())

Note that this gives you the systemRoot certificates. if you need the user than simply change the value in the security command

Matan Benita
  • 196
  • 1
  • 4
4

As an update and datapoint, I ran into this issue running Python 3.7.0 on macOS 10.13.4:

$ ipython
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.0.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import bokeh.sampledata

In [2]: bokeh.sampledata.download()
Using data directory: /Users/me/.bokeh/data

...
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1045)

Instructions for solving the problem are in /Applications/Python\ 3.7/ReadMe.rtf

Following the suggestion there and running /Applications/Python\ 3.7/Install\ Certificates.command solved the problem:

From the terminal:

$ /Applications/Python\ 3.7/Install\ Certificates.command

Re-starting IPython...

$ ipython
>>> import bokeh.sampledata

>>> bokeh.sampledata.download()
Using data directory: /Users/me/.bokeh/data
Downloading: CGM.csv (1589982 bytes)
   1589982 [100.00%]
...
JS.
  • 14,781
  • 13
  • 63
  • 75
1

For me /Applications/Python\ 3.6/./Install\ Certificates command fails on pip certifi install. I am on mac High Sierra and use python3 so pip somewhat fails and I have to use pip3 instead.

So here what I did:

  1. Manually ran pip3 install --update certify in a shell
  2. Remove the install certifi line from the command script
  3. Reran the script and everything was fine.

Note that you will end up with a cert.pem symbolic link in: /Library/Frameworks/Python.framework/Versions/3.6/etc/openssl/

Prasoon Karunan V
  • 2,916
  • 2
  • 12
  • 26
Rocko
  • 19
  • 1
1

Solution for MacOS or Linux with latest Python versions installed either as standalone or via port or brew

Download certificates from Certifi project at https://github.com/certifi/python-certifi/blob/master/certifi/cacert.pem. FYI Certifi is a 3rd party library that provides Mozilla’s curated collection of Root Certificates for validating the trustworthiness of SSL certificates while verifying the identity of TLS hosts.

Then add to your ~/.zshrc on the latest MacOS or ~/.bash_profile or similar:

export SSL_CERT_FILE=/pathtodownloadedfile/cacert.pem
export REQUESTS_CA_BUNDLE=/pathtodownloadedfile/cacert.pem
igo
  • 6,359
  • 6
  • 42
  • 51
  • 1
    Python requests library depends on certifi lib. And use it certificates by default. No need to add it to the env variable path. – Matan Benita Jul 13 '22 at 05:49
  • Thanks igo, on Mac, this addresses the issue where openssl fails to build because mac certs are not loaded in python venv. – Cobra Mar 06 '23 at 20:08
1

In my case only installing "Install Certificates.command" solved this issue using MAC OS

Update SSL certificate with certifi (MacOS only)

All we would have to do is to run command with the following piece of code:

- Press "command + space" button or open Spotlight
- type "Install Certificates.command" 

What this command does is update our system’s SSL certificate directory for MacOS.

Mansi mishra
  • 442
  • 1
  • 5
  • 11