Error de verificación del certificado SSL3_GET_SERVER_CERTIFICATE en Python al solicitar (solo) * .google.com

Me he encontrado con un error realmente extraño que tiene que ver con SSL y python para google.com (o más generalmente creo que con dominios que tienen múltiples cadenas de certificados). Cuando bash realizar una solicitud a https://*.google.com/whatever , https://*.google.com/whatever el siguiente error:

 SSLError: ("bad handshake: Error([('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')],)",) while doing GET request to URL: https://google.com/ 

Lo que he hecho hasta ahora.

He pasado por muchos problemas tratando de hacer que esto funcione y estoy recurriendo a publicar en Stack Overflow ahora que no sé qué hacer. Esto es lo que he intentado:

  1. Noté que la date devolvió una fecha que estaba 2 minutos por detrás del tiempo real (potencialmente invalidando mi certificado). Arreglé esto asumiendo que validaría el certificado. Esto no solucionó el problema.

  2. Descubrí que Python 2.7.9 realizó una copia de respaldo de algunas bibliotecas SSL de Python 3. Actualicé Python 2.7.6 a 2.7.9 asumiendo las actualizaciones (que incluyen las correcciones enumeradas en este hilo: https://serverfault.com/questions/692110) error-with-python2-as-a-https-client-with-an-nginx-server-and-ssl-certificate-ch ) lo arreglaría. Sin suerte, el mismo error.

  3. Obviamente, establecer verify=False funciona, pero no estamos dispuestos a ceder a la seguridad, necesitamos verify=True para que funcione.

  4. curl https://google.com también funciona como se esperaba. Así es como sé que tiene que ver con Python.

Ambiente

 $ python -V Python 2.7.9 $ pip list | grep -e requests requests (2.9.1) $ uname-a # ubuntu 14.04 Linux staging.example.com 3.13.0-48-generic #80-Ubuntu SMP Thu Mar 12 11:16:15 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 

Ejemplo

Esto solo está sucediendo para los dominios de google sobre https. Aquí hay un ejemplo:

 $ ipython Python 2.7.9 (default, Jan 6 2016, 21:37:32) Type "copyright", "credits" or "license" for more information. IPython 4.0.1 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: import requests In [2]: requests.get('https://facebook.com', verify=True) Out[2]:  In [3]: requests.get('https://stackoverflow.com', verify=True) Out[3]:  In [4]: requests.get('https://spotify.com', verify=True) Out[4]:  In [5]: requests.get('http://google.com', verify=True) # notice the http Out[5]:  In [6]: requests.get('https://google.com', verify=True) --------------------------------------------------------------------------- SSLError Traceback (most recent call last)  in () ----> 1 requests.get('https://google.com', verify=True) /example/.virtualenv/example/lib/python2.7/site-packages/requests/api.pyc in get(url, params, **kwargs) 65 66 kwargs.setdefault('allow_redirects', True) ---> 67 return request('get', url, params=params, **kwargs) 68 69 /example/.virtualenv/example/lib/python2.7/site-packages/requests/api.pyc in request(method, url, **kwargs) 51 # cases, and look like a memory leak in others. 52 with sessions.Session() as session: ---> 53 return session.request(method=method, url=url, **kwargs) 54 55 /example/.virtualenv/example/lib/python2.7/site-packages/requests/sessions.pyc in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json) 466 } 467 send_kwargs.update(settings) --> 468 resp = self.send(prep, **send_kwargs) 469 470 return resp /example/.virtualenv/example/lib/python2.7/site-packages/requests/sessions.pyc in send(self, request, **kwargs) 574 575 # Send the request --> 576 r = adapter.send(request, **kwargs) 577 578 # Total elapsed time of the request (approximately) /example/.virtualenv/example/lib/python2.7/site-packages/requests/adapters.pyc in send(self, request, stream, timeout, verify, cert, proxies) 445 except (_SSLError, _HTTPError) as e: 446 if isinstance(e, _SSLError): --> 447 raise SSLError(e, request=request) 448 elif isinstance(e, ReadTimeoutError): 449 raise ReadTimeout(e, request=request) SSLError: ("bad handshake: Error([('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')],)",) 

Encontré una solución. Parece que hay un problema importante en la versión de certifi que se estaba ejecutando. Descubrí esto de este (muy largo) problema de GitHub: https://github.com/certifi/python-certifi/issues/26

TL; DR

pip uninstall -y certifi && pip install certifi==2015.04.28