2011-10-07 14 views
25

Devo esaminare un elenco di IP e recuperare il nome comune dal certificato su quell'IP (per ogni IP che consente le connessioni 443 della porta). Sono stato in grado di farlo con successo usando i socket e i moduli ssl. Funziona su tutti gli IP con certificati firmati validi ma non funziona per i certificati autofirmati.Come posso recuperare il certificato peer TLS/SSL di un host remoto usando python?

Se io uso questo metodo, si richiede un cert valida che si verifica dal mio CA-bundle:

from socket import socket 
import ssl 

s = socket() 
c = ssl.wrap_socket(s,cert_reqs=ssl.CERT_REQUIRED, ca_certs='ca-bundle.crt') 
c.connect(('127.0.0.1', 443)) 

print c.getpeercert() 

Se rimuovo il cert_reqs=ssl.CERT_REQUIRED quindi si connette, ma non ottiene il certificato a tutti.

Come posso recuperare il nome comune di un certificato su un indirizzo IP valido o meno rispetto al ca-bundle?

risposta

37

La libreria python ssl sembra che analizzi solo il certificato per te se ha una firma valida.

"""Returns a formatted version of the data in the 
    certificate provided by the other end of the SSL channel. 
    Return None if no certificate was provided, {} if a 
    certificate was provided, but not validated.""" 

È ancora possibile ottenere il certificato del server con la funzione ssl.get_server_certificate(), ma lo restituisce in formato PEM. (In alternativa, si potrebbe chiamare c.getpeercert(True), che restituisce il CERT in formato DER binario, se è convalidato o meno.)

>>> print ssl.get_server_certificate(('server.test.com', 443)) 
-----BEGIN CERTIFICATE----- 
MIID4zCCAsugAwIBA..... 

Da qui, vorrei utilizzare M2Crypto o OpenSSL per leggere il CERT e ottenere valori:

# M2Crypto 
cert = ssl.get_server_certificate(('www.google.com', 443)) 
x509 = M2Crypto.X509.load_cert_string(cert) 
x509.get_subject().as_text() 
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com' 

# OpenSSL 
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) 
x509.get_subject().get_components() 
#[('C', 'US'), 
# ('ST', 'California'), 
# ('L', 'Mountain View'), 
# ('O', 'Google Inc'), 
# ('CN', 'www.google.com')] 
+1

Vale anche la pena notare che se si estrae il certificato tramite ssl.getpeercert (True), è necessario caricarlo utilizzando OpenSSL.crypto.FILETYPE_ASN1 anziché FILETYPE_PEM –

+3

Poiché la vulnerabilità "barboncino" in SSLv3 molti server Web l'hanno disabilitata. Potrebbe essere necessario aggiungere 'ssl_version = ssl.PROTOCOL_TLSv1' alla tua chiamata 'get_server_certificate (..)' se vedi qualcosa come "sslv3 alert handshake failure" –

+0

M2Crypto chiama SocketServer, che diventa socketserver sotto python3. M2Crypto si romperà sotto python3. – mootmoot

4

Su Mac è necessario installare sorso e M2Crypto

Sul terminale eseguire:

brew install swig 

E poi:

sudo pip install m2crypto 

Quindi è possibile eseguire il codice di cui sopra:

from socket import socket 
import ssl 
import M2Crypto 
import OpenSSL 

# M2Crypto 
cert = ssl.get_server_certificate(('www.google.com', 443)) 
x509 = M2Crypto.X509.load_cert_string(cert) 
print x509.get_subject().as_text() 
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com' 

# OpenSSL 
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) 
print x509.get_subject().get_components() 
#[('C', 'US'), 
# ('ST', 'California'), 
# ('L', 'Mountain View'), 
# ('O', 'Google Inc'), 
# ('CN', 'www.google.com')]