web-dev-qa-db-fra.com

openssl, python demande une erreur: "la vérification du certificat a échoué"

Si j'exécute la commande suivante à partir de ma boîte de développement:

$ openssl s_client -connect github.com:443

J'obtiens la dernière ligne de sortie suivante:

Verify return code: 20 (unable to get local issuer certificate)

Si j'essaie de le faire avec des demandes, j'obtiens une autre demande ayant échoué:

>>> import requests
>>> r = requests.get('https://github.com/', verify=True)

Avec une exception soulevée:

SSLError: [Errno 1] _ssl.c:507: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

Je peux également exécuter la première commande avec l'indicateur de vérification et obtenir une sortie similaire:

$ openssl s_client -connect github.com:443 -verify 9
...
Verify return code: 27 (certificate not trusted)

Fondamentalement, cela me dit qu'il y a un problème avec les certificats. Je peux spécifier un certificat spécifique avec les deux méthodes et cela fonctionnera:

$ openssl s_client -connect github.com:443 -CAfile /etc/ssl/certs/DigiCert_High_Assurance_EV_Root_CA.pem -verify 9
...
Verify return code: 0 (ok)

et:

>>> r = requests.get('https://github.com/', verify='/etc/ssl/certs/DigiCert...pem')
<Response [200]>

Donc, à ma question, qu'est-ce qui ne va pas exactement ici? Les requêtes/openssl ne devraient-ils pas déjà savoir où trouver les certificats valides?

Autre info:

  • Python == 2.7.6
  • demandes == 2.2.1
  • openssl 0.9.8h

De plus, je sais que passer verify=False à la requests.get la méthode fonctionnera aussi, mais je veux vérifier.

[~ # ~] modifier [~ # ~]

J'ai confirmé que, comme @Heikki Toivonen l'a indiqué dans une réponse, spécifier l'indicateur -CAfile pour la version d'openssl que j'utilise fonctionne.

$ openssl s_client -connect github.com:443 -CAfile `python -c 'import requests; print(requests.certs.where())'`
...
Verify return code: 0 (ok)

Il n'y a donc rien de mal avec la version d'openssl que j'utilise, et il n'y a rien de mal avec le fichier cacert.pem par défaut fourni par request.

Maintenant que je sais que openssl est censé fonctionner de cette façon, que le fichier CA ou l'endroit où trouver les certificats doit être spécifié, je suis plus préoccupé par les demandes de travail.

Si je cours:

>>> r = requests.get('https://github.com/', verify='path to cacert.pem file')

Je reçois toujours la même erreur qu'auparavant. J'ai même essayé de télécharger le fichier cacert.pem depuis http://curl.haxx.se/ca et cela n'a toujours pas fonctionné. les demandes ne semblent fonctionner (sur cette machine spécifique) que si je spécifie un fichier de certificat de fournisseur spécifique.

Une remarque: sur ma machine locale, tout fonctionne comme prévu. Il existe cependant plusieurs différences entre les deux machines. Jusqu'à présent, je n'ai pas pu déterminer quelle est la différence spécifique à l'origine de ce problème.

21
chadgh

Si j'exécute la commande suivante à partir de ma boîte de développement:

$ openssl s_client -connect github.com:443

J'obtiens la dernière ligne de sortie suivante:

Verify return code: 20 (unable to get local issuer certificate)

Tu es absent DigiCert High Assurance EV CA-1 comme racine de confiance:

$ openssl s_client -connect github.com:443
CONNECTED(00000003)
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV CA-1
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/serialNumber=5157550/street=548 4th Street/postalCode=94107/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=github.com
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
---
Server certificate
...
Start Time: 1393392088
Timeout   : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)

Télécharger DigiCert High Assurance EV CA-1 de Certificats d'autorité racine de confiance DigiCert :

$ wget https://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt
--2014-02-26 00:27:50--  https://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt
Resolving www.digicert.com (www.digicert.com)... 64.78.193.234
...

Convertissez le certificat codé DER en PEM:

$ openssl x509 -in DigiCertHighAssuranceEVCA-1.crt -inform DER -out DigiCertHighAssuranceEVCA-1.pem -outform PEM

Ensuite, utilisez-le avec OpenSSL via le -CAfile:

$ openssl s_client -CAfile DigiCertHighAssuranceEVCA-1.pem -connect github.com:443
CONNECTED(00000003)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV CA-1
verify return:1
depth=0 businessCategory = Private Organization, 1.3.6.1.4.1.311.60.2.1.3 = US, 1.3.6.1.4.1.311.60.2.1.2 = Delaware, serialNumber = 5157550, street = 548 4th Street, postalCode = 94107, C = US, ST = California, L = San Francisco, O = "GitHub, Inc.", CN = github.com
verify return:1
---
Certificate chain
 0 s:/businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/serialNumber=5157550/street=548 4th Street/postalCode=94107/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=github.com
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIHOjCCBiKgAwIBAgIQBH++LkveAITSyvjj7P5wWDANBgkqhkiG9w0BAQUFADBp
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSgwJgYDVQQDEx9EaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBDQS0xMB4XDTEzMDYxMDAwMDAwMFoXDTE1MDkwMjEyMDAwMFowgfAxHTAb
BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMTAlVT
MRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQFEwc1MTU3NTUwMRcw
FQYDVQQJEw41NDggNHRoIFN0cmVldDEOMAwGA1UEERMFOTQxMDcxCzAJBgNVBAYT
AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv
MRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xEzARBgNVBAMTCmdpdGh1Yi5jb20wggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt04nDXXByCfMzTxpydNm2WpVQ
u2hhn/f7Hxnh2gQxrxV8Gn/5c68d5UMrVgkARWlK6MRb38J3UlEZW9Er2TllNqAy
GRxBc/sysj2fmOyCWws3ZDkstxCDcs3w6iRL+tmULsOFFTmpOvaI2vQniaaVT4Si
N058JXg6yYNtAheVeH1HqFWD7hPIGRqzPPFf/jsC4YX7EWarCV2fTEPwxyReKXIo
ztR1aE8kcimuOSj8341PTYNzdAxvEZun3WLe/+LrF+b/DL/ALTE71lmi8t2HSkh7
bTMRFE00nzI49sgZnfG2PcVG71ELisYz7UhhxB0XG718tmfpOc+lUoAK9OrNAgMB
AAGjggNUMIIDUDAfBgNVHSMEGDAWgBRMWMsl8EFPUvQoyIFDm6aooOaS5TAdBgNV
HQ4EFgQUh9GPGW7kh29TjHeRB1Dfo79VRyAwJQYDVR0RBB4wHIIKZ2l0aHViLmNv
bYIOd3d3LmdpdGh1Yi5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG
AQUFBwMBBggrBgEFBQcDAjBjBgNVHR8EXDBaMCugKaAnhiVodHRwOi8vY3JsMy5k
aWdpY2VydC5jb20vZXZjYTEtZzIuY3JsMCugKaAnhiVodHRwOi8vY3JsNC5kaWdp
Y2VydC5jb20vZXZjYTEtZzIuY3JsMIIBxAYDVR0gBIIBuzCCAbcwggGzBglghkgB
hv1sAgEwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9z
c2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4A
eQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQA
ZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUA
IABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAA
YQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcA
cgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIA
aQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQA
ZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMH0G
CCsGAQUFBwEBBHEwbzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
Y29tMEcGCCsGAQUFBzAChjtodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
aUNlcnRIaWdoQXNzdXJhbmNlRVZDQS0xLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG
SIb3DQEBBQUAA4IBAQBfFW1nwzrVo94WnEUzJtU9yRZ0NMqHSBsUkG31q0eGufW4
4wFFZWjuqRJ1n3Ym7xF8fTjP3fdKGQnxIHKSsE0nuuh/XbQX5DpBJknHdGFoLwY8
xZ9JPI57vgvzLo8+fwHyZp3Vm/o5IYLEQViSo+nlOSUQ8YAVqu6KcsP/e612UiqS
+UMBmgdx9KPDDzZy4MJZC2hbfUoXj9A54mJN8cuEOPyw3c3yKOcq/h48KzVguQXi
SdJbwfqNIbQ9oJM+YzDjzS62+TCtNSNWzWbwABZCmuQxK0oEOSbTmbhxUF7rND3/
+mx9u8cY//7uAxLWYS5gIZlCbxcf0lkiKSHJB319
-----END CERTIFICATE-----
subject=/businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/serialNumber=5157550/street=548 4th Street/postalCode=94107/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=github.com
issuer=/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV CA-1
---
No client certificate CA names sent
---
SSL handshake has read 4139 bytes and written 446 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 59D2883BBCE8E81E63E5551FAE7D1ACC00C49A9473C1618237BBBB0DD9016B8D
    Session-ID-ctx: 
    Master-Key: B6D2763FF29E77C67AD83296946A4D44CDBA4F37ED6F20BC27602F1B1A2D137FACDEAC862C11279C01095594F9776F79
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1393392673
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)

Les requêtes/openssl ne devraient-ils pas déjà savoir où trouver les certificats valides?

Non. OpenSSL ne fait confiance à rien par défaut. C'est un opposé polaire au modèle d'un navigateur, où presque tout est approuvé par défaut.


 $ openssl s_client -connect github.com:443 -CAfile `python -c 'import requests; print(requests.certs.where())'`
 ...
 >>> r = requests.get('https://github.com/', verify='path to cacert.pem file')

Pourquoi feriez-vous confiance à des centaines d'autorités de certification et d'autorités de certification subordonnées (re: cacert.pem) lorsque vous connaissez la seule autorité de certification qui certifie la clé publique du site? Faites confiance à la seule racine requise et rien de plus: DigiCert High Assurance EV CA-1.

Tout faire confiance - comme dans le modèle du navigateur - est ce qui a permis à Comodo Hacker d'usurper des certificats pour Gmail, Hotmail, Yahoo, etc. lorsque la racine Diginotar a été compromise.

18
jww

À partir de la demande 2.4.0, l'auteur recommande en utilisant certifi , qui est une collection de certificats racine. Il y a un paquet python pour cela:

pip install certifi
7
ecstaticpeon

openssl s_client par défaut n'utilisera pas le fichier de certificats CA avec lequel il est livré, mais il essaie de vérifier la connexion. C'est la raison pour laquelle votre test échoue sans aucun paramètre et fonctionne avec -CAfile.

De même, Requests essaie de vérifier la connexion par défaut, mais il semble qu'il ne sache pas où se trouvent les certificats CA. Cela peut être un problème de configuration dans votre environnement lors de la construction/installation d'OpenSSL, Python ou Requests. Je dis cela parce que le site Web Requests montre votre exemple de travail contre https: // github. com sans avoir besoin de définir le chemin de l'AC.

1
Heikki Toivonen