2011-10-07 20 views
25

Necesito escanear a través de una lista de IP y recuperar el nombre común del certificado en esa IP (para cada IP que permita las conexiones del puerto 443). He podido hacer esto con éxito utilizando los sockets y los módulos ssl. Funciona para todas las direcciones IP con certificados válidos y firmados, pero no funciona para los certificados autofirmados.¿Cómo puedo recuperar el certificado de par TLS/SSL de un host remoto utilizando python?

Si utilizo este método, se requiere un certificado válido que es verificado por mi CA-paquete:

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() 

Si quito el cert_reqs=ssl.CERT_REQUIRED continuación, se conecta pero no recibe el certificado en absoluto.

¿Cómo puedo recuperar el nombre común para un certificado en un IP si se valida contra el paquete ca o no?

Respuesta

37

La biblioteca python ssl parece que solo analiza el certificado por usted si tiene una firma válida.

"""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.""" 

Puede aún así obtener el certificado del servidor con la función ssl.get_server_certificate(), pero lo devuelve en formato PEM. (Alternativamente, se podría llamar c.getpeercert(True), que devuelve el certificado en formato DER binario, ya sea validada o no.)

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

A partir de aquí, me gustaría utilizar M2Crypto o OpenSSL para leer el certificado y obtener valores:

# 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

También vale la pena señalar que si extrae el certificado a través de ssl.getpeercert (Verdadero), debe cargarlo usando OpenSSL.crypto.FILETYPE_ASN1 en lugar de FILETYPE_PEM –

+3

Dado que la vulnerabilidad "Poodle" en SSLv3 muchos servidores web lo han deshabilitado. Es posible que deba agregar 'ssl_version = ssl.PROTOCOL_TLSv1' a su llamada' get_server_certificate (..) 'si ve algo como" fallo de handshake de alerta de sslv3 " –

+0

M2Crypto llame a SocketServer, que cambia a socketserver en python3. M2Crypto se romperá bajo python3. – mootmoot

4

en Mac es necesario instalar trago y M2Crypto

El terminal ejecutar:

brew install swig 

Y luego:

sudo pip install m2crypto 

continuación, puede ejecutar el código anterior:

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')] 
Cuestiones relacionadas