Curl works but python request fails with SSLError
Turns out the issue was with the certificate.
I was having self signed certificates which were not present in the OS trust store.
Python requests need the path to full chain cert not just intermediate cert for verify
parameter. See requests documentation: SSL Cert Verification
After updating it, it worked without any issues.
Python requests SSL error - certificate verify failed
As already pointed out in a comment: the site has a bad SSL implementation as can be seen from the SSLLabs report. The main part of this report regarding your problem is:
This server's certificate chain is incomplete. Grade capped to B.
This means that the server is not sending the full certificate chain as is needed to verify the certificate. This means you need to add the missing certificates yourself when validating. For this you need to include the PEM for the missing chain certificate C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA and also for the root CA C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA info a file my_trust_store.pem
and then you can call:
requests.get("https://...", verify='my_trust_store.pem')
... but I've tried downloading the site's certificate and pointing to that file using the verify option
This will not work with normal leaf certificates. Since the SSL stack of Python is based on OpenSSL and OpenSSL expects only trusted certificate authorities in the trust store (i.e. given with verify
) and a server certificate is not CA certificate it will not help to add it to the trust store.
Python Requests throwing SSLError after downloading certificate
The issue was indeed that I needed to download the certificate chain, and not just the individual certificate. I do not know how to do this with openssl as I would like, but I was able to do it in Firefox.
This is done by going to the website, clicking on the padlock next to the web address, clicking on the arrow next to the message "Connection secure", clicking "More Information", going to the "Security" tab, clicking on "View Certificate", then clicking on the download "PEM (chain)" in the "Miscellaneous" section. This is the certificate I needed to make requests.get() work properly.
python Requests SSL ERROR (certificate verify failed)
It is highly recommended to have a deeper look at the excellent documentation for requests. It has a special chapter about SSL Cert Validation which explains:
You can pass verify the path to a CA_BUNDLE file or directory with certificates of trusted CAs:
>>> requests.get('https://github.com', verify='/path/to/certfile')
Assuming that your server certificate was signed by your ca.crt
you should use this for the verify
parameter.
EDIT: based on the discussion it looks like that CA and server certificate used the same subject. This means that the certificate validation assumes that this is a self-signed certificate which thus results in an certificate validation error.
python requests to google.com throwing SSLError
I figured it out! TL;DR: my antivirus was doing some HTTPS/TLS filtering.
I had a look at the answer that @Juilen Cochennec linked in his comment, again, and noticed it suggested using openssl directly from the commendline to examine what certificate chain was actually being returned.
So I ran
openssl s_client -showcerts -connect googleapis.com:443
and I did not get the certificate chain I expected; the first intermediate certificate was not issued by Google but by Bullguard. It was
i:/C=GB/ST=Hounslow/L=Heathrow/O=BullGuard Ltd./OU=DevelTeam/CN=BullGuard SSL Proxy CA
Bullguard is the name of my antivirus, so I suspected that it was somehow blocking my requests. After some research I found the precise cause, namely, Bullguard's Safe Browsing feature. It scans websites and blocks them if it thinks they are unsafe.
A subset of this is safe results, which shows checkmarks (or X's) next to results on search engines, such as Google. It appears to use HTTPS/TLS filtering to do this; it inserts itself between you and your search results so that it can modify the page with the checkmarks. Looking now, this was not even secure in Firefox; the checkmarks don't display because it can't get the safe browsing results.
The upshot is that this filtering was causing SSL errors across all google domains, which was tripping up requests. To fix all I had to do was disable safe results for Google; not even safe browsing itself, just safe results. This disabled the filtering and my requests now work again.
I hope that this helps anyone else with this problem!
P.S. I found this useful to see what the SSL certificate chain was supposed to be, which helped me to identify that it was wrong.
Python Requests throws SSL Error on certain site
... lot of people claimed adding in the optional argument "verify=False" should fix these types of SSL errors
adding verify=False
helps against errors when validating the certificate, but not against EOF from server, handshake errors or similar.
As can be seen from SSLLabs this specific server exhibits the behavior of simply closing the connection (i.e. "EOF occurred in violation of protocol") for clients which don't support TLS 1.2 with modern ciphers. While you don't specify which SSL version you use I expect it to be a version less than OpenSSL 1.0.1, the first version of OpenSSL supporting TLS 1.2.
Please check ssl.OPENSSL_VERSION
for the version used in your code. If I'm correct your only fix is to upgrade the version of OpenSSL use by Python. How this is done depends on your platform but there are existing posts about it, like Updating openssl in python 2.7.
Related Topics
Converting Between Datetime, Timestamp and Datetime64
How to Remove an Element from a List by Index
What Is the Python Equivalent of Static Variables Inside a Function
How to Pass a String into Subprocess.Popen (Using the Stdin Argument)
Manually Raising (Throwing) an Exception in Python
How to Check If a String Is a Substring of Items in a List of Strings
How to Prevent Tensorflow from Allocating the Totality of a Gpu Memory
Adding Python to Path on Windows
Why Does the Expression 0 ≪ 0 == 0 Return False in Python
How to Prettyprint a Json File
Error Message: "'Chromedriver' Executable Needs to Be Available in the Path"
How to Iterate Over Files in a Given Directory
Difference Between Map, Applymap and Apply Methods in Pandas