python - libreria - urllib2 readline
Error SSL Pyll Urllib2 (1)
Para resumir los comentarios sobre la causa del problema y explicar el problema real con más detalle:
Si verifica la cadena de confianza para el cliente OpenSSL, obtiene lo siguiente:
[0] 54:7D:B3:AC:BF:... /CN=*.s3.amazonaws.com
[1] 5D:EB:8F:33:9E:... /CN=VeriSign Class 3 Secure Server CA - G3
[2] F4:A8:0A:0C:D1:... /CN=VeriSign Class 3 Public Primary Certification Authority - G5
[OT] A1:DB:63:93:91:... /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
El primer certificado [0] es el certificado de hoja enviado por el servidor. Los siguientes certificados [1] y [2] son certificados de cadena enviados por el servidor. El último certificado [OT] es el certificado raíz de confianza, que no es enviado por el servidor pero está en el almacenamiento local de CA de confianza. Cada certificado de la cadena está firmado por el siguiente y el último certificado [OT] es confiable, por lo que la cadena de confianza está completa.
Si comprueba la cadena de confianza en su lugar mediante un navegador (por ejemplo, Google Chrome usando la biblioteca NSS) obtendrá la siguiente cadena:
[0] 54:7D:B3:AC:BF:... /CN=*.s3.amazonaws.com
[1] 5D:EB:8F:33:9E:... /CN=VeriSign Class 3 Secure Server CA - G3
[NT] 4E:B6:D5:78:49:... /CN=VeriSign Class 3 Public Primary Certification Authority - G5
Aquí [0] y [1] son enviados nuevamente por el servidor, pero [NT] es el certificado raíz de confianza. Si bien esto se ve exactamente como el certificado de cadena [2], la huella dactilar dice que los certificados son diferentes. Si echa un vistazo más de cerca a los certificados [2] y [NT], verá que la clave pública dentro del certificado es la misma y, por lo tanto, [2] y [NT] pueden usarse para verificar la firma de [ 1] y, por lo tanto, se puede utilizar para construir la cadena de confianza.
Esto significa que, si bien el servidor envía la misma cadena de certificados en todos los casos, hay varias formas de verificar la cadena hasta un certificado raíz de confianza. Cómo se hace esto depende de la biblioteca SSL y de los certificados raíz confiables conocidos:
[0] (*.s3.amazonaws.com)
|
[1] (Verisign G3) --------------------------/
| |
/------------------ [2] (Verisign G5 F4:A8:0A:0C:D1...) |
| |
| certificates sent by server |
.....|...............................................................|................
| locally trusted root certificates |
| |
[OT] Public Primary Certification Authority [NT] Verisign G5 4E:B6:D5:78:49
OpenSSL library Google Chrome (NSS library)
Pero la pregunta sigue siendo por qué su verificación no fue exitosa. Lo que hizo fue tomar el certificado raíz de confianza utilizado por el navegador (Verisign G5 4E: B6: D5: 78: 49) junto con OpenSSL. Pero la verificación en el navegador (NSS) y OpenSSL funcionan ligeramente diferente:
- NSS: crea una cadena de confianza a partir de los certificados enviados por el servidor. Deje de construir la cadena cuando obtengamos un certificado firmado por cualquiera de los certificados raíz de confianza local.
- OpenSSL_ crea una cadena de confianza a partir de los certificados enviados por el servidor. Una vez hecho esto, verifique si tenemos un certificado raíz de confianza que firme el último certificado de la cadena.
Debido a esta sutil diferencia, OpenSSL no puede verificar la cadena [0], [1], [2] contra el certificado raíz [NT], porque este certificado no firma el último elemento en la cadena [2] sino [1] . Si el servidor solo enviara una cadena de [0], [1], entonces la verificación tendría éxito.
Este es un
error conocido desde
hace
mucho tiempo
y existen
patches
y es de esperar que el problema finalmente se aborde en OpenSSL 1.0.2 con la introducción de la opción
X509_V_FLAG_TRUSTED_FIRST
.
Python 2.7.9 ahora es mucho más estricto sobre la verificación de certificados SSL. ¡Increíble!
No me sorprende que los programas que funcionaban antes ahora reciban CERTIFICATE_VERIFY_FAILED errores. Pero parece que no puedo hacer que funcionen (sin deshabilitar la verificación del certificado por completo).
Un programa estaba usando urllib2 para conectarse a Amazon S3 a través de https.
Descargo el certificado de CA raíz en un archivo llamado "verisign.pem" e intento esto:
import urllib2, ssl
context = ssl.create_default_context()
context.load_verify_locations(cafile = "./verisign.pem")
print context.get_ca_certs()
urllib2.urlopen("https://bucket.s3.amazonaws.com/", context=context)
y sigo obteniendo errores CERTIFICATE_VERIFY_FAILED, a pesar de que la CA raíz se imprime correctamente en la línea 4.
openssl puede conectarse a este servidor bien. De hecho, aquí está el comando que usé para obtener el certificado de CA:
openssl s_client -showcerts -connect bucket.s3.amazonaws.com:443 < /dev/null
Tomé el último certificado de la cadena y lo puse en un archivo PEM, que se abre bien. Es un certificado de Verisign con:
Serial number: 35:97:31:87:f3:87:3a:07:32:7e:ce:58:0c:9b:7e:da
Subject key identifier: 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
SHA1 fingerprint: F4:A8:0A:0C:D1:E6:CF:19:0B:8C:BC:6F:BC:99:17:11:D4:82:C9:D0
¿Alguna idea de cómo hacer que esto funcione con la validación habilitada?