Requests: SSLError "mal apretón de manos: error ([('rutinas SSL', 'tls_process_server_certificate', 'verificación de certificado fallida')],) con certificado autofirmado

Creado en 10 nov. 2017  ·  12Comentarios  ·  Fuente: psf/requests

Hola. Estoy tratando de conectarme a un servidor con https. Tengo el certificado, que es un certificado autofirmado que se incluye en el parámetro de verificación, pero el resultado es un error de "verificación de certificado fallida". Sospechaba que tenía que ver con el certificado autofirmado (por Microsoft IIS), pero con curl esto funciona.
¡Gracias por adelantado!

Esta es la salida de openssl:

openssl s_client -showcerts -connect server:44300
CONNECTED(00000003)
depth=0 CN = server
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = server
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=server
   i:/CN=server
-----BEGIN CERTIFICATE-----
<certificate data here>
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=server
issuer=/CN=server
---
No client certificate CA names sent
Peer signing digest: SHA1
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1477 bytes and written 431 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: CC4A000083B1E03B446416C9C0B16CBEAB79949E3CF5C936A309A6F92FA01364
    Session-ID-ctx:
    Master-Key: 798A570B0EC2A0CBB7C4C4DE6167E7579A92239942D869CD794B8BEBEA6EB5E492394634AD32665A8BB829DE1F3858D2
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510329948
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

Resultado Esperado

Espero, dado que el certificado está en el parámetro de verificación, que la conexión no falle. Si intento lo mismo con curl fuera de python, funciona:

curl https://server:44300 --cacert /usr/share/ca-certificates/server.crt
 HTTP/1.1 403 Forbidden
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 1158
Content-Type: text/html
Server: Microsoft-IIS/10.0
X-Frame-Options: SAMEORIGIN
P3P: CP=None
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Requested-With,Content-Type
Access-Control-Allow-Credentials: true
Date: Fri, 10 Nov 2017 16:02:26 GMT

(Falla pero no por problemas de certificado)

Resultado actual

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 72, in get
    return request('get', url, params=params, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/adapters.py", line 506, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='nlybstqvp4nb75n.code1.emi.philips.com', port=44300): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

Pasos de reproducción

import requests
requests.get('https://server:44300', verify='/usr/share/ca-certificates/server.crt')

Información del sistema

$ python -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": "2.1.3"
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "2.7.12"
  },
  "platform": {
    "release": "4.10.0-38-generic",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "1010007f",
    "version": "17.3.0"
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "1000207f"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": true
}

Este comando solo está disponible en Requests v2.16.4 y superior. De lo contrario,
proporcione información básica sobre su sistema (versión de Python,
sistema operativo, etc.).

Comentario más útil

Si es un principiante en el módulo Python requests y quiere hacer algunas cosas que requieren el acceso a sitios seguros, existe una alta probabilidad de que este error lo condene: Certificate verify failed y, como todos los programadores principiantes, tendrá la tentación de usar auth = session.post( mysecureurl, verify=false)

Pero esta es una práctica muy mala y se ha desaconsejado en muchas publicaciones de SO, pero aún así los principiantes hacen un mal uso de esto porque el error es muy difícil de corregir.
Permítanme intentar arrojar algo de luz sobre este tema.
Python (pip) y Conda y cualquier software basado en Python utiliza un almacén de certificados separado como lo hacen todos los navegadores. La biblioteca de solicitudes de Python usa su propio archivo CA de forma predeterminada o usará el paquete de certificados del paquete certifi si está instalado. Además, pip no usa los certificados del sistema a diferencia de curl .
Por lo tanto, para requests debe especificar manualmente el almacén de certificados a través de conda o pip.

Tldr;

  1. Exporte toda la cadena de certificados codificados .cer utilizando el navegador según este increíble blog que se muestra aquí. Tenga en cuenta que ese blog no se trata de conda certstore sino de git certstore y solo dice que exporte la raíz, sin embargo, exporté todas las cadenas de certificados en archivos separados .
  2. Luego, instale certifi usando el comando pip install certifi
  3. Verifique la ruta predeterminada del almacén de certificados de conda o python:

import ssl
ssl.get_default_verify_paths() O
import certifi
certifi.where()

  1. Una vez que haya localizado el archivo cacert.pem predeterminado, ábralo (preferiblemente en el Bloc de notas++) y agregue todo el certificado al final del archivo. (Cuide la demarcación del certificado -----BEGIN CERTIFICATE----- y -----END CERTIFICATE----- ). Guarda el archivo y listo.
    O si está usando conda, use los comandos de conda:
    conda config --set ssl_verify <pathToYourFile>.crt
    (He notado que este comando actualiza cosas en C:\Users\johndoe\.condarc )

  2. Use el siguiente código para verificar:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

Además, si está en Linux, puede exportar cacert personalizado a todo el sistema o perfil de usuario ( .bashrc o .bash_profile ) usando este enlace .

Todos 12 comentarios

Por cierto, esto es Ubuntu 16.04

Es casi seguro que este error se deba a que el certificado en sí no es válido de alguna manera. ¿Puede proporcionar el certificado codificado con PEM, por favor?

Hola. Gracias por la rápida respuesta. Este es el certificado:
-----INICIAR CERTIFICADO-----
MIID3TCCAsWgAwIBAgIIU/nMdlbWojMwDQYJKoZIhvcNAQELBQAwMDEuMCwGA1UE
AwwlTkxZQlNUUVZQNE5CNzVOLmNvZGUxLmVtas5waGlsaXBzLmNvbTAeFw0xNzEx
MTAwMDAwMDBaFw0yNzExMTAwMDAwMDBaMDAxLjAsBgNVBAMMJU5MWUJTVFFWUDRO
Qjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC3mzWep6k1/FbkzzoyZ8QBy/tE8adfwKvw80zaLL+coche1bBZ9U
VIXs4es3babtjD3QJWP5/mwoBdIp8gvQkjA1X7RBNJZXbPz6hGR4eqaeRQLrFV9Y
TtB92MA9CDpCXfalCvzzO1jw3zvP1BHUdnTQEwSnnwtf/Ryaud+e7TDxGq8LThmc
glZgO8d2zaYpIjwWx92bXDF/qlqWBkH5mtKIkWOw6Y71xz0Di62cFrMAPEGBjK3c
szpBa5Ttb9+SFtl16t2xDyCbiPFkoMW/4u3Husy/i18hLhEuQwZMHnWsocm+etZ4
8fDt5Bqhab8zC+LKS+Ll7qZdqMHzobeB6j5JAgMBAAGjgfowgfcwXwYDVR0jBFgw
VoAUGdJ3os9nPtTubuwcy1ugtDMdSMChNKQyMDAxLjAsBgNVBAMMJU5MWUJTVFFW
UDROQjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb22CCFP5zHZW1qIzMB0GA1UdDgQW
BBQZ0neiz2c+1O5u7BzLW6C0Mx1IwDAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIE
sDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwiwOwYDVR0RBDQwMoIlTkxZ
QlNUUVZQNE5CNzVOLmNvZGUxLmVtas5waGlsaXBzLmNvbYIJbG9jYWxob3N0MA0G
CSqGSIb3DQEBCwUAA4IBAQC0MtflowNB4LTLKD1MW3w0QIY5ale3/sEhNCQgHGN5
iNZJptFuFt5jgPGmFjy0Pb5vLMS/Ag1RF3UgTeZzFnaSgyB4mTnwj1gLdwQidVcr
2SlL7TffCj0m/bYjtNbwExRqXE4pQKb5RKwYwpruZaX/G3oHWOG9+2X9Pw5C42zB
OFE4KvYUwOV+noabXvil8LERIdKYxR/2B6qBiwm47IcioqM07zTYLHJ+WDTEMO2k
Qy51yXwFmeOEr5MIBElYCQ0j2AfI4RCXr+2cyUym7tjEr3/I8EsZ5Crvdf++Gwaz
2A05ScPMr+5yfVXygZCenMTwNAyUY1yN9zVj8/n94Psa
-----FIN DEL CERTIFICADO-----
Sé que no es del todo válido. Pero de alguna manera curl es capaz de aceptarlo. Me preguntaba por qué las solicitudes no hacen lo mismo. Asumí que ambos usan openssl para validar el certificado.

¿Alguna actualización sobre esto? También tengo problemas con los sitios certificados de autofirma en la biblioteca de solicitudes de Python.

Obtener debajo del error.

solicitudes.get('https://10.10.24.20', verificar='/etc/ssl/certs/certSIGN_ROOT_CA.pem')
Rastreo (llamadas recientes más última):
Expediente "", línea 1, en
Archivo "/usr/local/lib/python2.7/dist-packages/requests/api.py", línea 72, en get
solicitud de devolución('get', url, params=params, *kwargs)Archivo "/usr/local/lib/python2.7/dist-packages/requests/api.py", línea 58, en solicitudvolver session.request(método=método, url=url, * kwargs)
Archivo "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", línea 508, en solicitud
resp = self.send(prep, *send_kwargs)Archivo "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", línea 618, en envíor = adaptador. enviar (solicitud, * kwargs)
Archivo "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", línea 506, en envío
aumentar SSLError (e, solicitud = solicitud)
solicitudes.excepciones.SSLError: HTTPSConnectionPool(host='10.10.24.20', port=443): Se excedieron los reintentos máximos con url: / (Causado por SSLError(SSLError("proceso de comunicación incorrecto: Error([('rutinas SSL', 'tls_process_server_certificate ', 'verificación del certificado fallida')],)",),))

Si mantengo verificar Falso, funciona pero lo quiero con verificar = Verdadero

No había visto esto antes, así que abrí un nuevo número, lo siento. Pero estoy enfrentando el mismo problema. Para mí, la 'solicitud' falla incluso con verificar = Falso.

$ pitón
Python 2.7.13 (predeterminado, 19 de enero de 2017, 14:48:08)
[CCG 6.3.0 20170118] en linux2
Escriba "ayuda", "derechos de autor", "créditos" o "licencia" para obtener más información.

solicitudes de importación
solicitudes.get("https://localhost:9000/getcpuinfo", verificar=Falso)
Rastreo (llamadas recientes más última):
Expediente "", línea 1, en
Archivo "/usr/lib/python2.7/dist-packages/requests/api.py", línea 70, en get
solicitud de devolución('get', url, params=params, *kwargs)Archivo "/usr/lib/python2.7/dist-packages/requests/api.py", línea 56, en solicitudvolver session.request(método=método, url=url, * kwargs)
Archivo "/usr/lib/python2.7/dist-packages/requests/sessions.py", línea 488, en solicitud
resp = self.send(prep, *send_kwargs)Archivo "/usr/lib/python2.7/dist-packages/requests/sessions.py", línea 609, en envíor = adaptador. enviar (solicitud, * kwargs)
Archivo "/usr/lib/python2.7/dist-packages/requests/adapters.py", línea 497, en envío
aumentar SSLError (e, solicitud = solicitud)
solicitudes.excepciones.SSLError: ("Error de enlace incorrecto: SysCallError(-1, 'EOF inesperado')",)

La información de mi certificado local es,

$ openssl s_client -showcerts -connect localhost:9000
CONNECTED(00000003)
depth=0 CN = localhost
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = localhost
verify return:1
write:errno=0
---
Certificate chain
 0 s:/CN=localhost
   i:/CN=localhost
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIJAKATu2AY/QT4MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xNzExMDkyMTQ1NTBaFw0xNzEyMDkyMTQ1NTBaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAJTPk5nao0wG/EDFGnq7BvXMkEZ5oUVq7PAUxWi+E/byJk924l7Z5kACgWBa
zQL0lLXLpdMk97EFGWMblz5Ehtqh7U8HaE9OZ6x/pesDTka+REnpXecklRrdZHX7
lfFnNIU58grPpB2GyUXrRdOtcPlaKXUo+VTd7PgwMtYVtt8pyTWxSB2MMYrqJGT8
78KX6trRzQLm7tas3U0jD59+R8j7gxU6FyFaNJBrkJ5T9kHGKOsAzSqZdCgRBjl5
i7xcXJfOAAnZ3jhGlY5DQht+HZDHhjkLG9kcZZhFDYteFk8drzbd3lBw96nLq+8A
Sy92FtQL4GiYSwZ0WVAmwmTCGjUCAwEAAaNTMFEwHQYDVR0OBBYEFLYjwGKbcV9h
sYHxe8l9UvXVivByMB8GA1UdIwQYMBaAFLYjwGKbcV9hsYHxe8l9UvXVivByMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHl7okBCJlms+cwfzLhs
sbyyuX2wgngxyvjy497zBmeh1TiueGPhOx9u/sfJSZmoUaeRd/zPGkp2DcPQ+Lo2
EHYrXMPE1Ecgpu/15JZ8jNuE+FwZb9lllULLwzq8pDkdbdsSRltdV/rFlZ2YkscB
c+xvVaCltw5KpKnY6AWHoqwoDcd8TZKzyKXLSuluKbHNC1lvg8cMzs6hFA9P92Ae
9P08AKLAIOGJ7QzRrXQIsAO4p9rHheeZeYQZyNiRrXPQUoWos4+OjynaNs+FabhN
XBtSl/GGPRRRfU/D9v4iKfQx15CEvs1AKn1Z6mIPF05pSqbgIoz2mJBV6UM7e+hz
TRs=
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=localhost
issuer=/CN=localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1198 bytes and written 302 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 5311B8500C8AF327083E1465FE1E1A6A98E0996B4791150A01D6B130C7F0549909A4BDCDED388E9EDE124BB6C50E150A
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510599077
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: no
---

Por favor, no secuestre este problema. En general, debe usar Stack Overflow para hacer estas preguntas: si sobrecarga el problema con solicitudes, simplemente me inclina a cerrarlo.

@sg77 Su certificado está marcado CA=FALSE , lo que lo hace inelegible para ser un certificado emisor. Sospecho que curl está personalizando su código para usar este certificado como un pin, en lugar de como una CA raíz. Las solicitudes no hacen eso: este certificado no puede ser una CA raíz, por lo que no se valida.

Recomiendo acuñar un nuevo certificado con CA=TRUE u omitir las BasicConstraints por completo.

@ashwini-kaklij No tengo idea de por qué falla su verificación, porque no puedo ver el certificado. No lo publique aquí : dirija su pregunta a StackOverflow en su lugar.

@uttampawar Su error se debe a que al servidor no le gusta nuestro protocolo de enlace TLS por algún motivo. A falta de más detalles, no puedo determinar por qué. Por favor, nuevamente, lleve su pregunta a Stack Overflow.

@Lukasa No quise secuestrar el problema solo elevarlo. Puede ser que este sea un lugar equivocado. Agregué mis comentarios y observación ya que vi que es de naturaleza similar. Agradezco sus comentarios. Preguntaré en stackoverflow. Gracias.

Por favor, no secuestre este problema. En general, debe usar Stack Overflow para hacer estas preguntas: si sobrecarga el problema con solicitudes, simplemente me inclina a cerrarlo.

@sg77 Su certificado está marcado CA=FALSE , lo que lo hace inelegible para ser un certificado emisor. Sospecho que curl está personalizando su código para usar este certificado como un pin, en lugar de como una CA raíz. Las solicitudes no hacen eso: este certificado no puede ser una CA raíz, por lo que no se valida.

Recomiendo acuñar un nuevo certificado con CA=TRUE u omitir las BasicConstraints por completo.

@ashwini-kaklij No tengo idea de por qué falla su verificación, porque no puedo ver el certificado. Por favor, _no lo publique aquí_: dirija su pregunta a StackOverflow en su lugar.

@uttampawar Su error se debe a que al servidor no le gusta nuestro protocolo de enlace TLS por algún motivo. A falta de más detalles, no puedo determinar por qué. Por favor, nuevamente, lleve su pregunta a Stack Overflow.

Hola Lukasa - Acabo de ver tu respuesta. Ha respondido a @sg77 diciendo "Su certificado está marcado CA=FALSE ".

¿Cómo se dice que esto es falso y dónde/cómo puedo volver a establecerlo en VERDADERO?

Gracias.

publicar la clave del certificado en línea es como publicar su contraseña

@synida

publicar la clave del certificado en línea es como publicar su contraseña

Umm no. Publicar la clave privada del certificado es equivalente a su contraseña.

Cada CA en el negocio tiene su certificado en todos los sistemas operativos y navegadores principales. ¿Están dando su contraseña a todo el mundo?

https://en.wikipedia.org/wiki/Public-key_cryptography

Es casi seguro que este error se deba a que el certificado en sí no es válido de alguna manera. ¿Puede proporcionar el certificado codificado con PEM, por favor?

¿Cómo puedo acceder a este certificado?

Si es un principiante en el módulo Python requests y quiere hacer algunas cosas que requieren el acceso a sitios seguros, existe una alta probabilidad de que este error lo condene: Certificate verify failed y, como todos los programadores principiantes, tendrá la tentación de usar auth = session.post( mysecureurl, verify=false)

Pero esta es una práctica muy mala y se ha desaconsejado en muchas publicaciones de SO, pero aún así los principiantes hacen un mal uso de esto porque el error es muy difícil de corregir.
Permítanme intentar arrojar algo de luz sobre este tema.
Python (pip) y Conda y cualquier software basado en Python utiliza un almacén de certificados separado como lo hacen todos los navegadores. La biblioteca de solicitudes de Python usa su propio archivo CA de forma predeterminada o usará el paquete de certificados del paquete certifi si está instalado. Además, pip no usa los certificados del sistema a diferencia de curl .
Por lo tanto, para requests debe especificar manualmente el almacén de certificados a través de conda o pip.

Tldr;

  1. Exporte toda la cadena de certificados codificados .cer utilizando el navegador según este increíble blog que se muestra aquí. Tenga en cuenta que ese blog no se trata de conda certstore sino de git certstore y solo dice que exporte la raíz, sin embargo, exporté todas las cadenas de certificados en archivos separados .
  2. Luego, instale certifi usando el comando pip install certifi
  3. Verifique la ruta predeterminada del almacén de certificados de conda o python:

import ssl
ssl.get_default_verify_paths() O
import certifi
certifi.where()

  1. Una vez que haya localizado el archivo cacert.pem predeterminado, ábralo (preferiblemente en el Bloc de notas++) y agregue todo el certificado al final del archivo. (Cuide la demarcación del certificado -----BEGIN CERTIFICATE----- y -----END CERTIFICATE----- ). Guarda el archivo y listo.
    O si está usando conda, use los comandos de conda:
    conda config --set ssl_verify <pathToYourFile>.crt
    (He notado que este comando actualiza cosas en C:\Users\johndoe\.condarc )

  2. Use el siguiente código para verificar:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

Además, si está en Linux, puede exportar cacert personalizado a todo el sistema o perfil de usuario ( .bashrc o .bash_profile ) usando este enlace .

¿Fue útil esta página
0 / 5 - 0 calificaciones