¿Cómo se resuelven los nombres de los servidores de certificados SSL? ¿Puedo agregar nombres alternativos usando keytool?
Estas pueden formularse como preguntas separadas para mayor claridad, pero todas están relacionadas con el mismo tema.
¿Cómo se resuelven los nombres de los servidores de certificados SSL?
¿Por qué los navegadores parecen utilizar el campo CN del certificado, pero el mecanismo de Java parece mirar únicamente los "nombres alternativos de los sujetos"?
¿Es posible agregar nombres alternativos a un certificado SSL usando keytool? Si no es así, ¿usar openSSL es una buena opción?
Solo un poco de historia: necesito conseguir que un servidor principal se comunique con varios servidores mediante HTTPS. Obviamente, no queremos comprar certificados SSL para cada servidor (podría haber muchos), así que quiero usar certificados autofirmados (he estado usando keytool para generarlos). Después de agregar los certificados como confiables en el sistema operativo, los navegadores (IE y Chrome) aceptan felizmente la conexión como confiable. Sin embargo, incluso después de agregar los certificados a los cacerts de Java, Java aún no acepta la conexión como confiable y genera la siguiente excepción:
Causado por: java.security.cert.CertificateException: no hay nombres alternativos de sujeto presentes en sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142) en sun.security.util.HostnameChecker.match(HostnameChecker.java:75) en com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509T rustManagerImpl.java:264) en com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:250) en com. sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Clien tHandshaker.java:1185) ... 14 más
Descubrí que puedo hacer que Java confíe en el certificado que implementa mi propio HostNameVerifier, que copié desde aquí: com.sun.jbi.internal.security.https.DefaultHostnameVerifier solo para probar (por cierto, el nombre de host pasado como argumento al HostnameVerifier es correcto, así que creo que debería haberse aceptado).
He estado usando el campo del certificado CN como nombre de host (generalmente la dirección IP).
¿Alguien puede decirme si estoy haciendo algo mal y orientarme en la dirección correcta?
La forma en que se debe realizar la verificación del nombre de host se define en RFC 6125 , que es bastante reciente y generaliza la práctica a todos los protocolos, y reemplaza al RFC 2818 , que era específico de HTTPS. (Ni siquiera estoy seguro de que Java 7 use RFC 6125, que podría ser demasiado reciente para esto).
Del RFC 2818 (Sección 3.1) :
Si está presente una extensión sujetoAltName de tipo dNSName, DEBE usarse como identidad. De lo contrario, DEBE utilizarse el campo Nombre común (más específico) en el campo Asunto del certificado. Aunque el uso del nombre común es una práctica existente, está en desuso y se recomienda a las autoridades de certificación que utilicen el nombre dNS en su lugar.
[...]
En algunos casos, el URI se especifica como una dirección IP en lugar de un nombre de host. En este caso, la dirección IP sujetoAltName debe estar presente en el certificado y debe coincidir exactamente con la IP en el URI.
Esencialmente, el problema específico que tiene proviene del hecho de que está utilizando direcciones IP en su CN y no un nombre de host. Algunos navegadores pueden funcionar porque no todas las herramientas siguen estrictamente esta especificación, en particular porque "más específico" en RFC 2818 no está claramente definido (consulte las discusiones en RFC 6215).
Si está utilizando keytool
, a partir de Java 7,keytool
tiene una opción para incluir un Nombre alternativo del sujeto (consulte la tabla en la documentación para -ext
): puede usar -ext san=dns:www.example.com
o -ext san=ip:10.0.0.1
.
EDITAR:
Puede solicitar una SAN en OpenSSL cambiándola openssl.cnf
(seleccionará la copia en el directorio actual si no desea editar la configuración global, hasta donde recuerdo, o puede elegir una ubicación explícita usando la OPENSSL_CONF
variable de entorno).
Configure las siguientes opciones (busque primero las secciones apropiadas entre paréntesis):
[req]
req_extensions = v3_req
[ v3_req ]
subjectAltName=IP:10.0.0.1
# or subjectAltName=DNS:www.example.com
También hay un buen truco para usar una variable de entorno para esto (en lugar de arreglarlo en un archivo de configuración) aquí: http://www.crsr.net/Notes/SSL.html