Requests: SSLError "mauvaise poignée de main : erreur ([('routines SSL', 'tls_process_server_certificate', 'échec de la vérification du certificat')],) avec un certificat auto-signé

Créé le 10 nov. 2017  ·  12Commentaires  ·  Source: psf/requests

Salut. J'essaie de me connecter à un serveur en https. J'ai le certificat qui est un certificat auto-signé qui est inclus dans le paramètre de vérification mais le résultat est une erreur "échec de la vérification du certificat". Je soupçonnais que cela avait à voir avec le certificat auto-signé (par Microsoft IIS) mais avec curl cela fonctionne.
Merci d'avance!

Voici la sortie d'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)
---

résultat attendu

Je m'attends, puisque le certificat est dans le paramètre de vérification, à ce que la connexion n'échoue pas. Si j'essaie la même chose avec curl en dehors de python, cela fonctionne:

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

(Cela échoue mais pas à cause de problèmes de certificat)

Résultat actuel

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')],)",),))

Étapes de reproduction

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

Informations système

$ 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
}

Cette commande est uniquement disponible sur les demandes v2.16.4 et supérieures. Autrement,
veuillez fournir quelques informations de base sur votre système (version Python,
système d'exploitation, etc.).

Commentaire le plus utile

Si vous êtes débutant dans le module python requests et que vous souhaitez faire des choses qui nécessitent d'accéder à des sites sécurisés, il y a de fortes chances que vous soyez condamné par cette erreur - Certificate verify failed et comme tous les programmeurs débutants, vous sera tenté d'utiliser auth = session.post( mysecureurl, verify=false)

Mais c'est une très mauvaise pratique et a été découragée par de nombreux messages SO, mais les débutants en abusent encore car l'erreur est si difficile à corriger.
Permettez-moi d'essayer de jeter un peu de lumière sur cette question.
Python (pip) et Conda et tout logiciel basé sur python utilisent un magasin de certificats séparé, comme tous les navigateurs. La bibliothèque Python Requests utilise son propre fichier CA par défaut, ou utilisera le bundle de certificats du package certifi s'il est installé. De plus, pip n'utilise pas les certificats système contrairement à curl .
Par conséquent, pour le requests , vous devez spécifier manuellement le magasin de certificats via le conda ou le pip.

Tldr ;

  1. Exportez toute la chaîne de certificats encodés .cer à l'aide du navigateur conformément à cet incroyable blog présenté ici. Notez que ce blog ne concerne pas conda certstore mais git certstore et il dit seulement d'exporter la racine, cependant j'ai exporté toutes les chaînes de certificats dans des fichiers séparés .
  2. Ensuite, installez certifi en utilisant la commande pip install certifi
  3. Vérifiez le chemin par défaut du magasin de certificats de conda ou python :

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

  1. Une fois que vous avez localisé le fichier cacert.pem par défaut, ouvrez-le (de préférence dans Notepad++) et ajoutez tout le certificat à la fin du fichier. (Attention à la démarcation des certificats -----BEGIN CERTIFICATE----- et -----END CERTIFICATE----- ). Enregistrez le fichier et c'est tout.
    Ou si vous utilisez conda, utilisez les commandes conda :
    conda config --set ssl_verify <pathToYourFile>.crt
    (J'ai remarqué que cette commande met à jour des éléments dans C:\Users\johndoe\.condarc )

  2. Utilisez le code ci-dessous pour vérifier :
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

De plus, si vous êtes sous Linux, vous pouvez exporter un cacert personnalisé dans un profil système ou utilisateur ( .bashrc ou .bash_profile ) en utilisant ce lien .

Tous les 12 commentaires

BTW, c'est Ubuntu 16.04

Cette erreur est presque certainement due au fait que le certificat lui-même est invalide d'une manière ou d'une autre. Pouvez-vous fournir le certificat encodé PEM lui-même, s'il vous plaît ?

Salut. Merci pour la réponse rapide. Voici le certificat :
-----COMMENCER LE CERTIFICAT-----
MIID3TCCAsWgAwIBAgIIU/nMdlbWojMwDQYJKoZIhvcNAQELBQAwMDEUMCwGA1UE
AwwlTkxZQlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbTAeFw0xNzEx
MTAwMDAwMDBaFw0yNzExMTAwMDAwMDBaMDAxLjAsBgNVBAMMJU5MWUJTVFFWUDRO
Qjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC3mzWep6k1/FbkzzoyZ8QBy/tE8adfwKvw80zaLL+car1bBZ9U
VIXs4es3babtjD3QJWP5/mwoBdIp8gvQkjA1X7RBNJZXbPz6hGR4eqaeRQLrFV9Y
TtB92MA9CDpCXfalCvzzO1jw3zvP1BHUdnTQEwSnnwtf/Ryaud+e7TDxGq8LThmc
glZgO8d2zaYpIjwWx92bXDF/qlqWBkH5mtKIkWOw6Y71xz0Di62cFrMAPEGBjK3c
szpBa5Ttb9+SFtl16t2xDyCbiPFkoMW/4u3Husy/i18hLhEuQwZMHnWsocm+etZ4
8fDt5Bqhab8zC+LKS+Ll7qZdqMHzobeB6j5JAgMBAAGjgfowgfcwXwYDVR0jBFgw
VoAUGdJ3os9nPtTubuwcy1ugtDMdSMChNKQyMDAxLjAsBgNVBAMMJU5MWUJTVFFW
UDROQjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb22CCFP5zHZW1qIzMB0GA1UdDgQW
BBQZ0neiz2c+1O5u7BzLW6C0Mx1IwDAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIE
sDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwOwYDVR0RBDQwMoIlTkxZ
QlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbYIJbG9jYWxob3N0MA0G
CSqGSIb3DQEBCwUAA4IBAQC0MtflowNB4LTLKD1MW3w0QIY5ale3/sEhNCQgHGN5
inNZJptFuFt5jgPGmFjy0Pb5vLMS/Ag1RF3UgTeZzFnaSgyB4mTnwj1gLdwQidVcr
2SlL7TffCj0m/bYjtNbwExRqXE4pQKb5RKwYwpruZaX/G3oHWOG9+2X9Pw5C42zB
OFE4KvYUwOV+noabXvil8LERIdKYxR/2B6qBiwm47IcioqM07zTYLHJ+WDTEMO2k
Qy51yXwFmeOEr5MIBElYCQ0j2AfI4RCXr+2cyUym7tjEr3/I8EsZ5Crvdf++Gwaz
2A05ScPMr+5yfVXygZCenMTwNAyUY1yN9zVj8/n94Psa
-----CERTIFICAT DE FIN-----
Je sais que ce n'est pas tout à fait valable. Mais d'une manière ou d'une autre, curl est capable de l'accepter. Je me demandais pourquoi les demandes ne faisaient pas la même chose. J'ai supposé que les deux utilisent openssl pour valider le certificat.

Des mises à jour à ce sujet ? Je suis également confronté à un problème avec les sites certifiés auto-signés dans la bibliothèque de requêtes python.

Obtenir en dessous de l'erreur.

demandes.get('https://10.10.24.20', verify='/etc/ssl/certs/certSIGN_ROOT_CA.pem')
Traceback (dernier appel le plus récent) :
Fichier "", ligne 1, dans
Fichier "/usr/local/lib/python2.7/dist-packages/requests/api.py", ligne 72, dans get
return request('get', url, params=params, *kwargs)Fichier "/usr/local/lib/python2.7/dist-packages/requests/api.py", ligne 58, dans la requêtereturn session.request(method=method, url=url, * kwargs)
Fichier "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", ligne 508, dans la requête
resp = self.send(prep, *send_kwargs)Fichier "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", ligne 618, en envoir = adapter.send(demande, * kwargs)
Fichier "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", ligne 506, en envoi
lever SSLError(e, request=request)
requests.exceptions.SSLError : HTTPSConnectionPool(host='10.10.24.20', port=443) : nombre maximal de tentatives dépassé avec l'URL : / (causé par SSLError(SSLError("bad handshake : Error([('SSL routines', 'tls_process_server_certificate ', 'la vérification du certificat a échoué')],)",),))

Si je continue de vérifier False, cela fonctionne mais je le veux avec verify=True

Je ne l'avais pas vu avant, j'ai donc ouvert un nouveau sujet, désolé. Mais je suis confronté au même problème. Pour moi, 'request' échoue même avec verify=False.

$ python
Python 2.7.13 (par défaut, 19 janvier 2017, 14:48:08)
[GCC 6.3.0 20170118] sur Linux2
Tapez "aide", "copyright", "crédits" ou "licence" pour plus d'informations.

demandes d'importation
demandes.get("https://localhost:9000/getcpuinfo", verify=False)
Traceback (dernier appel le plus récent) :
Fichier "", ligne 1, dans
Fichier "/usr/lib/python2.7/dist-packages/requests/api.py", ligne 70, dans get
return request('get', url, params=params, *kwargs)Fichier "/usr/lib/python2.7/dist-packages/requests/api.py", ligne 56, dans la requêtereturn session.request(method=method, url=url, * kwargs)
Fichier "/usr/lib/python2.7/dist-packages/requests/sessions.py", ligne 488, dans la requête
resp = self.send(prep, *send_kwargs)Fichier "/usr/lib/python2.7/dist-packages/requests/sessions.py", ligne 609, en envoir = adapter.send(demande, * kwargs)
Fichier "/usr/lib/python2.7/dist-packages/requests/adapters.py", ligne 497, en envoi
lever SSLError(e, request=request)
requests.exceptions.SSLError : ("mauvaise poignée de main : SysCallError (-1, 'EOF inattendu')",)

Mes informations de certificat local sont,

$ 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
---

Merci de ne pas détourner ce problème. En général, vous devriez utiliser Stack Overflow pour poser ces questions : si vous surchargez le problème avec des requêtes, vous me donnez simplement envie de le fermer.

@sg77 Votre certificat est marqué CA=FALSE , ce qui le rend inéligible pour être un certificat émetteur. Je soupçonne que curl personnalise son code pour utiliser ce certificat comme code PIN, plutôt que comme autorité de certification racine. Les requêtes ne font pas cela : ce certificat ne peut pas être une autorité de certification racine, il ne valide donc pas.

Je recommande de créer un nouveau certificat avec CA=TRUE ou d'omettre complètement les BasicConstraints.

@ashwini-kaklij Je ne sais pas pourquoi votre vérification échoue, car je ne vois pas le certificat. Veuillez ne pas la publier ici : adressez plutôt votre question à StackOverflow.

@uttampawar Votre erreur est causée par le serveur qui n'aime pas notre poignée de main TLS pour une raison quelconque. En l'absence de plus de détails, je ne peux pas déterminer pourquoi. S'il vous plaît, encore une fois, posez votre question à Stack Overflow.

@Lukasa , je ne voulais pas détourner le problème, juste élever. Peut-être que c'est un mauvais endroit. J'ai ajouté mes commentaires et mes observations puisque j'ai vu que c'était de même nature. J'apprécie vos commentaires. Je vais demander sur stackoverflow. Merci.

Merci de ne pas détourner ce problème. En général, vous devriez utiliser Stack Overflow pour poser ces questions : si vous surchargez le problème avec des requêtes, vous me donnez simplement envie de le fermer.

@sg77 Votre certificat est marqué CA=FALSE , ce qui le rend inéligible pour être un certificat émetteur. Je soupçonne que curl personnalise son code pour utiliser ce certificat comme code PIN, plutôt que comme autorité de certification racine. Les requêtes ne font pas cela : ce certificat ne peut pas être une autorité de certification racine, il ne valide donc pas.

Je recommande de créer un nouveau certificat avec CA=TRUE ou d'omettre complètement les BasicConstraints.

@ashwini-kaklij Je ne sais pas pourquoi votre vérification échoue, car je ne vois pas le certificat. Veuillez _ne pas la publier ici_ : adressez plutôt votre question à StackOverflow.

@uttampawar Votre erreur est causée par le serveur qui n'aime pas notre poignée de main TLS pour une raison quelconque. En l'absence de plus de détails, je ne peux pas déterminer pourquoi. S'il vous plaît, encore une fois, posez votre question à Stack Overflow.

Salut Lukasa - Je viens de voir votre réponse. Vous avez répondu à @sg77 en disant "Votre certificat est marqué CA=FALSE ".

Comment savez-vous que c'est faux et où/comment puis-je le remettre à TRUE ?

Merci.

publier la clé de certificat en ligne revient à publier votre mot de passe

@Synida

publier la clé de certificat en ligne revient à publier votre mot de passe

Hum, non. La publication de la clé privée du certificat équivaut à votre mot de passe.

Chaque autorité de certification en entreprise a son certificat dans chaque système d'exploitation et navigateur grand public. Donnent-ils leur mot de passe à tout le monde ?

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

Cette erreur est presque certainement due au fait que le certificat lui-même est invalide d'une manière ou d'une autre. Pouvez-vous fournir le certificat encodé PEM lui-même, s'il vous plaît ?

comment accéder à ce certificat ?

Si vous êtes débutant dans le module python requests et que vous souhaitez faire des choses qui nécessitent d'accéder à des sites sécurisés, il y a de fortes chances que vous soyez condamné par cette erreur - Certificate verify failed et comme tous les programmeurs débutants, vous sera tenté d'utiliser auth = session.post( mysecureurl, verify=false)

Mais c'est une très mauvaise pratique et a été découragée par de nombreux messages SO, mais les débutants en abusent encore car l'erreur est si difficile à corriger.
Permettez-moi d'essayer de jeter un peu de lumière sur cette question.
Python (pip) et Conda et tout logiciel basé sur python utilisent un magasin de certificats séparé, comme tous les navigateurs. La bibliothèque Python Requests utilise son propre fichier CA par défaut, ou utilisera le bundle de certificats du package certifi s'il est installé. De plus, pip n'utilise pas les certificats système contrairement à curl .
Par conséquent, pour le requests , vous devez spécifier manuellement le magasin de certificats via le conda ou le pip.

Tldr ;

  1. Exportez toute la chaîne de certificats encodés .cer à l'aide du navigateur conformément à cet incroyable blog présenté ici. Notez que ce blog ne concerne pas conda certstore mais git certstore et il dit seulement d'exporter la racine, cependant j'ai exporté toutes les chaînes de certificats dans des fichiers séparés .
  2. Ensuite, installez certifi en utilisant la commande pip install certifi
  3. Vérifiez le chemin par défaut du magasin de certificats de conda ou python :

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

  1. Une fois que vous avez localisé le fichier cacert.pem par défaut, ouvrez-le (de préférence dans Notepad++) et ajoutez tout le certificat à la fin du fichier. (Attention à la démarcation des certificats -----BEGIN CERTIFICATE----- et -----END CERTIFICATE----- ). Enregistrez le fichier et c'est tout.
    Ou si vous utilisez conda, utilisez les commandes conda :
    conda config --set ssl_verify <pathToYourFile>.crt
    (J'ai remarqué que cette commande met à jour des éléments dans C:\Users\johndoe\.condarc )

  2. Utilisez le code ci-dessous pour vérifier :
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

De plus, si vous êtes sous Linux, vous pouvez exporter un cacert personnalisé dans un profil système ou utilisateur ( .bashrc ou .bash_profile ) en utilisant ce lien .

Cette page vous a été utile?
0 / 5 - 0 notes