2015-06-08 13 views
7

Sto lavorando all'implementazione di un'applicazione Web che utilizza un'API. Durante una risposta, il server API invia un collegamento a un certificato X509 (in formato PEM, composto da un certificato di firma e uno o più certificati intermedi in un certificato CA radice) che devo scaricare e utilizzare per effettuare ulteriori verifiche.Come convalidare/verificare una catena di attendibilità del certificato X509 in Python?

Prima di utilizzare il certificato, è necessario garantire che tutti i certificati della catena si combinino per creare una catena di trust con un certificato CA radice affidabile (per rilevare ed evitare richieste malevoli). Sto facendo fatica a farlo in Python e la mia ricerca sull'argomento non sta portando a nulla di utile.

Il certificato è facilmente afferrato e caricato utilizzando richieste e M2Crypto

import requests 
from M2Crypto import RSA, X509 

mypem = requests.get('https://server.com/my_certificate.pem') 
cert = X509.load_cert_string(str(mypem.text), X509.FORMAT_PEM) 

Tuttavia, convalidare la catena di certificati è un problema. Non è possibile per me scrivere il certificato su disco per utilizzare un'utilità della riga di comando come openssl tramite qualcosa come un sottoprocesso, quindi deve essere eseguito tramite python. Inoltre, non ho nessuna connessione aperta e quindi l'utilizzo di una soluzione di convalida basata sulla connessione (come menzionato in questa risposta/thread: https://stackoverflow.com/a/1088224/4984533) non funzionerà neanche.

su un altro thread su questo problema (a https://stackoverflow.com/a/4427081) Abate spiega che M2Crypto è incapace di fare questa convalida e dice che lui ha scritto una proroga per consentire la validazione (utilizzando il modulo m2ext), ma sua patch non sembra mai di lavoro, ritornando sempre falso, anche se so che è valida:

from m2ext import SSL 
ctx = SSL.Context() 
ctx.load_verify_locations(capath='/etc/ssl/certs/') # I have run c_rehash in this directory to generate a list of cert files with signature based names 
if not ctx.validate_certificate(cert): # always happens 
    print('Invalid certificate!') 

C'è anche questa risposta su un thread simile qui https://stackoverflow.com/a/9007764/4984533 in cui John Matthews sostiene di avere una patch scritta, che lo farà, ma purtroppo il link cerotto ora è morto - e comunque c'è un commento su quel thread che afferma che la patch non ha funzionato con openssl 0.9.8e.

Tutte le risposte relative alla convalida di una catena di certificati di trust in python sembrano essere collegate alla patch morta o tornare a m2ext.

Esiste un modo semplice e diretto per convalidare la catena di attendibilità dei certificati in Python?

+0

Btw pyOpenSSL avvolge OpenSSL quindi non si avrebbe bisogno di usarla dalla riga di comando. Vale anche la pena leggerlo se non hai già https://www.python.org/dev/peps/pep-0476/ –

+0

Grazie per il link Avi, non l'avevo mai visto prima. Non mi aiuta nella mia attuale situazione (sembra riguardare soprattutto i client http). Non ho problemi a usare un programma a riga di comando per effettuare la verifica se necessario, ma non se sono obbligato a scrivere il file pem su disco su ogni richiesta. Se il comando openssl verify potesse prendere una stringa raw, potrei usare quello che suppongo (anche se sembra una soluzione hacky per qualcosa che pensavo di sicuro sarebbe stato banale in Python). – speznot

risposta

4

Ho esaminato la libreria pyopenssl e l'ho trovata per la convalida della catena di certificati. L'esempio seguente è da their tests e sembra fare ciò che si desidera, che convalida la catena di attendibilità su un certificato radice attendibile. Qui ci sono i documenti rilevanti per X509Store and X509StoreContext

from OpenSSL.crypto import load_certificate, load_privatekey 
from OpenSSL.crypto import X509Store, X509StoreContext 
from six import u, b, binary_type, PY3 
root_cert_pem = b("""-----BEGIN CERTIFICATE----- 
MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE 
BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU 
ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2 
NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM 
MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U 
ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL 
urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy 
2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF 
1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE 
FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn 
VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE 
BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS 
b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB 
AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi 
hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY 
w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn 
-----END CERTIFICATE----- 
""") 
intermediate_cert_pem = b("""-----BEGIN CERTIFICATE----- 
MIICVzCCAcCgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQENBQAw 
WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAw 
DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTQw 
ODI4MDIwNDA4WhcNMjQwODI1MDIwNDA4WjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh 
dGUxDDAKBgNVBAoTA29yZzERMA8GA1UECxMIb3JnLXVuaXQxCzAJBgNVBAYTAlVT 
MQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2FuIERpZWdvMIGfMA0GCSqGSIb3DQEB 
AQUAA4GNADCBiQKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmK 
FGIbljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT 
21H2qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwID 
AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAPIWSkLX 
QRMApOjjyC+tMxumT5e2pMqChHmxobQK4NMdrf2VCx+cRT6EmY8sK3/Xl/X8UBQ+ 
9n5zXb1ZwhW/sTWgUvmOceJ4/XVs9FkdWOOn1J0XBch9ZIiFe/s5ASIgG7fUdcUF 
9mAWS6FK2ca3xIh5kIupCXOFa0dPvlw/YUFT 
-----END CERTIFICATE----- 
""") 
intermediate_server_cert_pem = b("""-----BEGIN CERTIFICATE----- 
MIICWDCCAcGgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQENBQAw 
ZjEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT 
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh 
biBEaWVnbzAeFw0xNDA4MjgwMjEwNDhaFw0yNDA4MjUwMjEwNDhaMG4xHTAbBgNV 
BAMTFGludGVybWVkaWF0ZS1zZXJ2aWNlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT 
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh 
biBEaWVnbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqpJZygd+w1faLOr1 
iOAmbBhx5SZWcTCZ/ZjHQTJM7GuPT624QkqsixFghRKdDROwpwnAP7gMRukLqiy4 
+kRuGT5OfyGggL95i2xqA+zehjj08lSTlvGHpePJgCyTavIy5+Ljsj4DKnKyuhxm 
biXTRrH83NDgixVkObTEmh/OVK0CAwEAATANBgkqhkiG9w0BAQ0FAAOBgQBa0Npw 
UkzjaYEo1OUE1sTI6Mm4riTIHMak4/nswKh9hYup//WVOlr/RBSBtZ7Q/BwbjobN 
3bfAtV7eSAqBsfxYXyof7G1ALANQERkq3+oyLP1iVt08W1WOUlIMPhdCF/QuCwy6 
x9MJLhUCGLJPM+O2rAPWVD9wCmvq10ALsiH3yA== 
-----END CERTIFICATE----- 
""") 

root_cert = load_certificate(FILETYPE_PEM, root_cert_pem) 
intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem) 
intermediate_server_cert = load_certificate(FILETYPE_PEM, intermediate_server_cert_pem) 
store = X509Store() 
store.add_cert(root_cert) 
store.add_cert(intermediate_cert) 
store_ctx = X509StoreContext(store, intermediate_server_cert) 
print(store_ctx.verify_certificate()) 
+0

Quindi, non riesco a far funzionare questo esempio: mi manca qualcosa? la chiamata verify_cert restituisce sempre 'None' (per il certificato fornito nell'esempio e il mio certificato con cui ho eseguito il test). Ho dovuto aggiungere l'importazione 'FILETYPE_PEM' nella parte superiore insieme alle altre tue importazioni da OpenSSL.crypto.Ho anche provato a rimuovere le newline estranee (dopo le righe "END CERTIFICATE") nelle stringhe CERT, ma non ne restituisce ancora nessuna. Grazie mille per i tuoi pensieri e il tempo su questo! – speznot

+0

Ah! Mi era sfuggito il commento fissandomi direttamente in faccia in quella documentazione che diceva che None è una risposta valida - le mie scuse. Ho provato a provare con i miei certificati e sto ottenendo "impossibile ottenere [certificato] issuer certificato" errori. Sulla riga di comando sto usando qualcosa di simile per verificare con successo: 'openssl verify -untrusted intermediate_cert.pem -CAfile rootcert.pem tovalidate.pem' (senza l'opzione -untrusted fallisce con errori simili che sto vedendo) - è correggi che nel tuo esempio 'intermediate_server_cert' è il cert che sto convalidando? – speznot

+0

Corretto, intermediate_server_cert è il certificato che viene convalidato in questo esempio. Penso che l'errore in generale significhi che un particolare certificato manca in qualche parte della catena. È strano, però, ho provato con un esempio diverso ed è riuscito a risolverlo. –

Problemi correlati