1

Trying to make a Python request to GCM API but certificates aren't verified.

requests and certifi packages are updated

  • Python 2.7.6
  • certifi==2017.4.17
  • requests==2.18.1
  • pyOpenSSL==17.1.0

Tried to call other servers and return are OK:

Error message: bad handshake: Error([('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')],)

Erwin Julius
  • 519
  • 5
  • 13
  • bad handshake has usually nothing to do with certificate validation but more with mismatch of TLS protocol version, ciphers, missing SNI, missing client certificates or similar. – Steffen Ullrich Jul 14 '17 at 19:43
  • @SteffenUllrich I've updated the exact error message. The question is not about certificate "validation", but verification. By the way, you're right, about nothing to do with certificate validation. The certificate mey be valid, but the program cannot verify it. – Erwin Julius Jul 15 '17 at 00:06
  • Thanks. It is always good to get the most detailed error description possible. Based on this I'm pretty sure that this is the exact problem as in the question I've added for duplicate, i.e. problems of your old OpenSSL version with alternative chain validation combined with missing the old root certificate in your store. The suggestion of @bpscott to use `certifi.old_where()` will probably help to work around this issue. – Steffen Ullrich Jul 15 '17 at 05:23

1 Answers1

1

I came across some interesting results when researching this problem. It seems to me that this could be the problem. Let me know.

TL;DR

Try using certifi.old_where(). If that works, then you really should upgrade to a newer version of OpenSSL on your server.

Sources

GitHub:

https://github.com/certifi/python-certifi/issues/32

From @Lukasa

Can you confirm whether or not this is the same problem as #26? That is, try passing certifi.old_where() to the verify argument of requests.

...

To be clear, there is no fix for this from Python-land other than using certifi.old_where() or upgrading OpenSSL. The OpenSSL on your system is too old to properly verify cross-signed TLS certificates, and three is no way for that problem to be resolved on my end. Your system is being put at significant risk if you use certifi.old_where() because you are continuing to base your trust on 1024-bit RSA certificates, which have been being deprecated since 2012 and are subject to several known attacks already.

certifi Docs:

https://pypi.python.org/pypi/certifi

1024-bit Root Certificates

Browsers and certificate authorities have concluded that 1024-bit keys are unacceptably weak for certificates, particularly root certificates. For this reason, Mozilla has removed any weak (i.e. 1024-bit key) certificate from its bundle, replacing it with an equivalent strong (i.e. 2048-bit or greater key) certificate from the same CA. Because Mozilla removed these certificates from its bundle, certifi removed them as well.

Unfortunately, old versions of OpenSSL (less than 1.0.2) sometimes fail to validate certificate chains that use the strong roots. For this reason, if you fail to validate a certificate using the certifi.where() mechanism, you can intentionally re-add the 1024-bit roots back into your bundle by calling certifi.old_where() instead. This is not recommended in production: if at all possible you should upgrade to a newer OpenSSL. However, if you have no other option, this may work for you.

bpscott
  • 484
  • 3
  • 15
  • certifi.old_where() worked well, but cannot upgrade OpenSSL right now. Amazon Linux is stuck on 1.0.1. I wiil change to another distribution with 1.0.2 support. Thank you. – Erwin Julius Jul 17 '17 at 14:45
  • Glad that helped you track down the problem – bpscott Jul 17 '17 at 16:59