requests: SSL Error: bad handshake
I could not use your lib in CeontOS 7 with Python 2.7.5. I got this error:
File "/usr/lib/python2.7/site-packages/requests/sessions.py", line 576, in send
r = adapter.send(request, **kwargs)
File "/usr/lib/python2.7/site-packages/requests/adapters.py", line 447, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')],)",)
Updating of Python or any SSL libs didn’t help. I’ve got this error in CentOS and Ubuntu, in Arch Linux everything works well.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 33 (17 by maintainers)
Aha, ok, we got there.
api.smartsheet.comserves its TLS using what’s known as a “cross-signed certificate”. This was used because Verisign, the CA forapi.smartsheet.com, originally used a 1024-bit root certificate. These were deprecated and replaced by stronger root certificates, but some older browsers and systems may not have received updates, so sites likeapi.smartsheet.comserve a root certificate that is signed by the 1024-bit root.That’s not normally a problem, except:
certifiremoved the weak 1024-bit rootsYou can solve this in two ways. The first, better but more drastic way, is to upgrade your OpenSSL to 1.0.2 or later. This is hard to do on Centos, I’m afraid. The less good but more effective way is to get the output of running
python -c "import certifi; print certifi.old_where()"and then set theREQUESTS_CA_BUNDLEenvironment variable to the printed path.@pensnarik Can you try running
pip install -U requests[security]in your environment and then try again?Yup, so the server is wrong.
This is a really common problem with TLS. A TLS certificate validation is performed by first building a certificate chain that takes us from a certificate the server can prove it has the private key for (the “leaf”) to a certificate we trust (the “root”). For OpenSSL there is an additional rule: the root must be self-signed.
In most cases, this chain is more than two steps long: that is, the leaf is not signed by the root, but is instead signed by a so-called intermediate certificate. That intermediate may itself be signed by the root, but may also be signed by other intermediates, until we eventually reach an intermediate that was signed by a root.
For example, for https://python-hyper.org, the chain is as follows:
The correct chain for the site you’re accessing should be:
For your use case, Requests ships with certificate number 3 in its trust database as a trusted root. However, your server is only sending certificate number 1. Requests cannot go from certificate 1 to certificate 3 without having certificate 2, and the server isn’t sending it. Most browsers ship with commonly-used intermediate certs, or can maintain caches of them, but Requests cannot do that, so it has no way of getting hold of certificate 2. Without certificate 2, validation must fail.
Properly-configured TLS servers send the leaf certificate and all of the necessary intermediaries. This is to ensure that clients that have never seen the intermediaries and that cannot dynamically fetch them are still able to validate the chain. As a comparison, check out the output of a command like yours run against
python-hyper.org:Note that the server sent two certificates in my case, whereas in yours it only sent one.
This is the error. Please reconfigure your server to send all the relevant intermediaries as well as the leaf.
While I’m here, I should note that running Qualys SSL Labs against your server would also have told you about this problem. =)
Downgrading certifi from 2015.11.20.1 to 2015.4.28 fixes the problem!
@sProject You would need to resolve both of those warnings before we can determine if you have a new problem. If you got requests from pip, run
pip install pyopenssl pyasn1 ndg-httpsclient. If you got Requests from your system provider, you should install those three packages from there.@Lukasa Thanks ! sudo pip install -U requests[security] worked for me.
GoDaddy is serving an incomplete certificate chain to you. That means we’re missing one of the intermediate certificates and can’t build up a trust chain. Either you’ll need to add the missing intermediary certificate to the certifi trust store or you’ll need to contact GoDaddy and tell them to sort their mess out.
Argh, the smartsheet SDK overrides that environment variable by explicitly using certifi.
New plan. Right at the start after you import smartsheet, before you do anything else, can you add the following lines?
That should hopefully help. You shouldn’t need the environment variable with this approach.