unable - ¿Cómo puedo recuperar el certificado de par TLS/SSL de un host remoto utilizando Python?
no module named m2crypto (3)
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.
Si utilizo este método, se requiere un certificado válido que mi paquete de CA verifique:
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 cert_reqs=ssl.CERT_REQUIRED
, se conecta pero no obtiene el certificado.
¿Cómo puedo recuperar el nombre común para un certificado en un IP si valida contra el paquete ca o no?
En Mac necesitas instalar Swig y M2Crypto
En la ejecución de la terminal:
brew install swig
Y entonces:
sudo pip install m2crypto
Entonces puedes ejecutar el código de arriba:
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'')]
La biblioteca python ssl parece que solo analiza el certificado por ti 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."""
Aún puede obtener el certificado del servidor con la función ssl.get_server_certificate()
, pero lo devuelve en formato PEM. (Alternativamente, puede llamar a c.getpeercert(True)
, que devuelve el certificado en formato binario DER, independientemente de si está validado o no).
>>> print ssl.get_server_certificate((''server.test.com'', 443))
-----BEGIN CERTIFICATE-----
MIID4zCCAsugAwIBA.....
Desde aquí, usaría 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'')]
Si alguien está luchando con SNI (Server Name Indication) (mencionado por @mootmoot), consulte mi respuesta aquí https://.com/a/49132495/8370670 .