94

here is my code

import requests;
url='that website';
headers={
  'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
  'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7',
  'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
};
r = requests.get(url,headers=headers);
print(r);
print(r.status_code);

then it ran into this:

requests.exceptions.SSLError:

HTTPSConnectionPool(host='www.xxxxxx.com', port=44 3):

Max retries exceeded with url: xxxxxxxx (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED]

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

what should i do?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
yuan
  • 1,701
  • 2
  • 13
  • 24
  • 2
    If you do have certificate, try `r = requests.get(url,headers=headers, cert=("/path/to/file.crt", "/path/to/file.key"))` – Andersson Aug 20 '18 at 07:54
  • 1
    Normally the python installation has access to root certificate authorities. However on some OSes such as OSX, the root CA are empty. Check this answer, maybe this helps: https://stackoverflow.com/a/56031239/1617295 – Raffi May 08 '19 at 05:20
  • I found this awesome article explaining the cause of it: https://medium.com/@superseb/get-your-certificate-chain-right-4b117a9c0fce – Jing He Nov 13 '20 at 15:19
  • Are/Were you on a Mac by any chance? Check out this answer on how to install certificates: https://stackoverflow.com/a/53310545/778533 – tommy.carstensen Jan 15 '21 at 17:03
  • 1
    Here is a [step-by-step tutorial on how to add missing certificates to `certifi` and hence to `Requests`](https://stackoverflow.com/a/66111417/516699). – Martin Hepp Feb 09 '21 at 01:13

12 Answers12

112

It's not recommended to use verify = False in your organization's environments. This is essentially disabling SSL verification.

Sometimes, when you are behind a company proxy, it replaces the certificate chain with the ones of Proxy. Adding the certificates in cacert.pem used by certifi should solve the issue. I had similar issue. Here is what I did, to resolve the issue -

  1. Find the path where cacert.pem is located -

Install certifi, if you don't have. Command: pip install certifi

import certifi
certifi.where()
C:\\Users\\[UserID]\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\certifi\\cacert.pem
  1. Open the URL on a browser. Download the chain of certificates from the URL and save as Base64 encoded .cer files.

  2. Now open the cacert.pem in a notepad and just add every downloaded certificate contents (---Begin Certificate--- *** ---End Certificate---) at the end.

Indranil
  • 1,776
  • 1
  • 17
  • 22
  • you recommend to install certifi to find a certificate inside its certifi-folder. can you please explain something about the backgrounds? – anion Aug 24 '19 at 14:36
  • 10
    Hello, it looks like Python uses certifi module for SSL communications. This certifi module uses cacert.pem file to validate against the SSL certificate. Only the certificates chains that are stored in cacert.pem are considered valid. When any SSL certificate is not found in this file, causes "CERTIFICATE_VERIFY_FAILED" error. – Indranil Sep 04 '19 at 19:51
  • All I had to do was `sudo pip install certifi` and that seemed to fix the issue for me. – Ben Davis Dec 16 '19 at 22:29
  • @BenDavis Yap, certifi would work as-is without any changes, if the machine is not behind any proxy, like ZScalar. – Indranil Jan 21 '20 at 17:40
  • 2
    If you get the error using urllib's `urlopen`, you can install certifi and then do `urlopen(url, cafile="/path/to/file.pem", capath="/path/to/certifi/").read()` – Vaulstein Jan 29 '20 at 13:49
  • 29
    So that other don't have to dig to figure out how to do Step 2: [How to download certificates from URL](https://medium.com/@menakajain/export-download-ssl-certificate-from-server-site-url-bcfc41ea46a2) – HamiltonPharmD Sep 13 '20 at 21:44
  • 3
    Step 2 with Chrome on a Mac https://stackoverflow.com/questions/25940396/how-to-export-certificate-from-chrome-on-a-mac – duff18 Oct 06 '20 at 20:23
  • Thanks! Your solution worked for FastAPI in Docker – incandenza Dec 11 '20 at 16:18
  • 11
    This worked for me too. Why must everything be a struggle to get the environment ready and working in python!! :-) – FMFF Mar 27 '21 at 05:25
  • @FMFF I think because Python Software Foundation is a nonprofit organization – bob Aug 25 '21 at 18:23
  • 2
    Even easier to get full certificate chain if you use Mozilla Browser: https://stackoverflow.com/a/69694605/12226311 – dgor Oct 24 '21 at 07:45
  • What url are you talking about? – Ice Bear Mar 16 '22 at 08:07
  • I have a bunch of certificates in my cacert.pem, all looking such as this: # Issuer: xxx # Subject: xxx # Label: "GlobalSign Root CA" # Serial: # MD5 Fingerprint: # SHA1 Fingerprint: # SHA256 Fingerprint: -----BEGIN CERTIFICATE----- xxxyyyzzz -----END CERTIFICATE----- I don't understand what should I do with this file. Do I save it as a .cer and then what? – Marco Di Gennaro Jun 21 '22 at 09:30
  • Updated instruction on how to download SSL Certificate: https://www.esri.com/arcgis-blog/products/bus-analyst/field-mobility/learn-how-to-download-a-ssl-certificate-for-a-secured-portal/ – Nemo Sep 07 '22 at 04:23
  • In a new VM that I'm working on, there are several CACERT.PEM files. I had to do this to cacert.pem specifically in F:\SRC\Python\env\Lib\site-packages\pip\_vendor\certifi env - being the environment. – FMFF Oct 19 '22 at 18:10
  • One more note - If you use Visual Studio 2022, anytime I refresh the requirement.txt file, the CACERT.PEM in your env folder gets refreshed too. So I have to repeat this. If anyone knows a work around for this, please share. – FMFF Oct 20 '22 at 00:41
  • Important for me was as @indranil said to get the CHAIN of certificates. Tried with just the single certificate, fail. With the chain, success. – Michael Wegter Dec 02 '22 at 17:08
  • I tried the solution still getting the error because the URL has "https" in it. Am I doing something wrong? – Manish Shegokar Mar 29 '23 at 12:31
29

If you have already tried to update the CA(root) Certificate using pip:

pip install --upgrade certifi

or have already downloaded the newest version of cacert.pem from https://curl.haxx.se/docs/caextract.html and replaced the old one in {Python_Installation_Location}\\lib\\site-packages\\certifi\\cacert.pem but it still does not work, then your client is probably missing the Intermediate Certificate in the trust chain.

Most browsers can automatically download the Intermediate Certificate using the URL in "Authority Info Access" section in the Certificate, but Python, Java, and openssl s_client cannot. They rely on the server proactively sending them the intermediate certificate.

Authority Infomation Access

If you speak Chinese you can read this awesome blog: https://www.cnblogs.com/sslwork/p/5986985.html and use this tool to check if the intermediate certificate is sent by / installed on the server or not: https://www.myssl.cn/tools/check-server-cert.html

If you do not, you can check this article: https://www.ssl.com/how-to/install-intermediate-certificates-avoid-ssl-tls-not-trusted/

We can also use openssl in Linux to cross-check this issue:

openssl s_client -connect yourwebsite:443

openssl: unable to get local issuer certificate The error message is even the same -- "unable to get local issuer certificate". I doubt that "local" here actually means "intermediate".

My current solution for this problem is like @Indranil's suggestion (https://stackoverflow.com/a/57466119/4522434): Export the Intermediate Certificate in browser using base64 X.509 CER format; then use Notepad++ to open it and copy the content into the end of cacert.pem in {Python_Installation_Location}\\lib\\site-packages\\certifi\\cacert.pem

Jing He
  • 794
  • 1
  • 9
  • 17
  • In the result of openssl command, CN = Common name, O = Organization, OU = Organization Unit, L = Locality, C = Country, S = State, ref link https://docs.oracle.com/cd/E24191_01/common/tutorials/authz_cert_attributes.html – Jing He Jan 04 '21 at 02:52
  • 2
    Useful to know about "Authority Info Access", thanks! – qris Sep 20 '21 at 15:18
28

Answers pointing to certifi are a good start and in this case there could be an additional step needed if on Windows.

pip install python-certifi-win32

The above package would patch the installation to include certificates from the local store without needing to manage store files manually. The patch was suggested to certifi but declined as "the purpose of certifi is not to be a cross-platform module to access the system certificate store." [https://github.com/certifi/python-certifi/pull/54#issuecomment-288085993]

The issue with local certificates traces to Python TLS/SSL and Windows Schannel. There is an open issue at Python [https://bugs.python.org/issue36011] and PEP that did not lead to a solution [https://www.python.org/dev/peps/pep-0543/#resolution]

Samuli P
  • 281
  • 3
  • 3
12

If you're using macOS, search for "Install Certificates.command" file (it is usually in Macintosh HD > Applications > your_python_dir).

You can also find it with "command" + "break space" and paste "Install Certificates.command" in the field.

If you used brew to install python, your solution is there: brew installation of Python 3.6.1: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

Quentin
  • 631
  • 6
  • 16
4

In macOS just open Macintosh HD

Now Select Application Then Select Python folder ( Python3.6, Python3.7 Whatever You are using just select this folder )

Then, double click on Install Certificates.command. Now your error should be solved.

3

I had the same problem. I was able to make requests against my server via the browser, but using python requests, I was getting the error mentioned above. Requests and certifi were both fully up to date; the problem ended up being my server's configuration.

The problem was that I had only installed the intermediate cert instead of the full cert chain.

In my case, following this article, I simply ran cat my-domain.crt my-domain.ca-bundle > my-domain.crt-combined and installed the crt-combined file on my server (via heroku's app settings interface) instead of the crt file.

jstaab
  • 3,449
  • 1
  • 27
  • 40
2

You can also set REQUESTS_CA_BUNDLE env variable to force requests library to use your cert, that solved my issue.

thegreyd
  • 66
  • 5
2

This should solve your problem

This is because the url is a https site instead of http. So it requires ssl verification using certificates. If you are working in your firms workstation, internal use sites will be accessible through the browser managed by your organization. The organization will have setup the certificates.

Atleast these certificates are needed

  • ROOT CA certificate
  • Intermediate CA certificate
  • Website ( domain ) certificate

The browsers will have these certificates configured, but python will not. So you need to do some manual work to get it working.

As Indranil suggests, using verify=False is not recommended. So download all the certificates as mentioned in the above link and follow the steps.

Narayan Bhat
  • 408
  • 3
  • 5
2

In my case the problem was that the server that I was trying to reach (which I also administrate) had a outdated CA file. When I updated to the correct new one the problem was solved. The point is: maybe is not a problem in your local code, but in the endpoint server.

1

For me the solution was quite easy:

  1. Download the certificate chain as PEM file. For that, I used Mozilla Firefox, viewed the certificate and clicked on the link "PEM (Chain)", see screenshot.

    enter image description here

  2. Following the documentation of requests, I added the verify parameter, i.e. it looked for me like requests.post(url, params, verify='/path/to/domain-chain.pem'), where domain-chain.pem is the file, that has been downloaded in step 1. The get function has also got the verify parameter. It is also possible, when using a session, see the example in the documentation.

Then it worked.

John
  • 1,472
  • 1
  • 19
  • 22
0

On my Windows machine behind a ZScaler proxy, none of the above solutions worked for me. I had to export and collect all my system certificates into a single PEM, which I made available for Python through the REQUESTS_CA_BUNDLE environment variable. I made the following PowerShell Core snippet to solve this.

ⓘ Note
This solution assumes you have openssl installed.
function Set-CaCertsBundles {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param(
        [Parameter()]
        [System.EnvironmentVariableTarget]
        $EnvVarTarget = [System.EnvironmentVariableTarget]::Machine,
        [Parameter()]
        [string]
        $CertOutPath = "$env:USERPROFILE\.certs\all.pem",
        [Parameter()]
        [switch]
        $FailFast
    )
    #Requires -Version 6.0
    begin {
        # Collect the certs from the local machine
        $certs = Get-ChildItem -Path Cert:\ -Recurse | Where-Object -FilterScript { $_.Thumbprint }
        $certItem = New-Item -Path ($CertOutPath | Split-Path -Parent) -Name ($CertOutPath | Split-Path -Leaf) -ItemType File -Confirm:$ConfirmPreference -Force  # Create if not exists
        if ($null -eq $certItem -and $WhatIfPreference) {
            $certItem = [System.IO.FileInfo]::new($CertOutPath)  # For WhatIf, indicates hypothetical output file (not created)
        }
        $envVars = 'GIT_SSL_CAINFO', 'AWS_CA_BUNDLE', 'CURL_CA_BUNDLE', 'NODE_EXTRA_CA_CERTS', 'REQUESTS_CA_BUNDLE', 'SSL_CERT_FILE'
    }
    process {
        for ($i = 0; $i -lt $certs.Count; $i++) {
            Write-Progress -Activity 'Copying certificates' -PercentComplete (100 * $i / $certs.Count)
            $thumbprintCrt = Join-Path -Path $env:TEMP -ChildPath "$($certs[$i].Thumbprint).crt"
            if (Test-Path -Path $thumbprintCrt -PathType Leaf) {
                $fs = [System.IO.FileStream]::new($thumbprintCrt, [System.IO.FileMode]::Open)
            }
            else {
                $fs = [System.IO.FileStream]::new($thumbprintCrt, [System.IO.FileMode]::Create) 
            }
            $fs.Write($certs[$i].RawData, 0, $certs[$i].RawData.Length)
            $fs.Close() && $fs.Dispose()
        
            openssl x509 -inform DER -in $thumbprintCrt -text | Add-Content -Path $certItem
            if ($LASTEXITCODE -ne 0 -and $FailFast) {
                Write-Error -Message 'Could not create last pem; ceasing to prevent continuous errors'
                return
            }
            Remove-Item -Path $thumbprintCrt
        }
    }
    end {
        for ($i = 0; $i -lt $envVars.Count; $i++) {
            Write-Progress -Activity 'Setting environment variables' -Status $envVars[$i] -PercentComplete (100 * $i / $envVars.Count)
            if ($PSCmdlet.ShouldProcess($envVars[$i], 'Set environment variable')) {
                [Environment]::SetEnvironmentVariable($envVars[$i], $certItem.FullName, $EnvVarTarget)
            }
        }
    }
}

I have reproduced this in the GitHub LogoGithub Gist.

Mavaddat Javid
  • 491
  • 4
  • 19
0

Another Way of Fixing This Error

settings.py

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

EMAIL_HOST_USER = env('SENDER_EMAIL') # email_address
EMAIL_HOST_PASSWORD = env('SENDER_EMAIL_PASSWORD') # app_password

Use Below command to install certifi

pip install certifi

--> my python version was 3.8 and certifi version was 2020.11.8 --> execute below command

mac user

/Applications/Python\ 3.8/Install\ Certificates.command

windows user

C:\your_full_path\Python38\Install Certificates.bat