¿Cómo se resuelven los nombres de los servidores de certificados SSL? ¿Puedo agregar nombres alternativos usando keytool?

Estos pueden redactarse como preguntas separadas para mayor claridad, pero todos están relacionados con el mismo problema.

¿Cómo se resuelven los nombres del servidor de certificados SSL?

¿Por qué los navegadores parecen usar el campo CN del certificado, pero el mecanismo de Java parece ver solo “nombres alternativos del sujeto”?

¿Es posible agregar nombres alternativos a un certificado SSL usando keytool? Si no, ¿usar openSSL es una buena opción?

Solo un poco de historia: necesito obtener un servidor principal para comunicarme con varios servidores usando 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 de confianza. Sin embargo, incluso después de agregar los certificados a las cacertas de Java, Java aún no aceptará la conexión como confiable y lanza 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 pasó como argumento al HostnameVerifier es correcto, así que creo que debería haber sido aceptado).

He estado usando el campo de certificado CN como el nombre de host (generalmente la dirección IP).

¿Puede alguien decirme por favor si estoy haciendo algo mal y señalarme en la dirección correcta?

La forma en que se debe hacer la verificación del nombre de host se define en RFC 6125 , que es bastante reciente y generaliza la práctica para todos los protocolos, y reemplaza a 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).

De RFC 2818 (Sección 3.1) :

Si está presente una extensión subjectAltName de tipo dNSName, DEBE utilizarse como identidad. De lo contrario, DEBE utilizarse el campo de 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 usen 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, iPAddress subjectAltName 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á usando 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 (ver 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 (vea 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 cambiando el openssl.cnf (escogerá la copia en el directorio actual si no desea editar la configuración global, por lo que recuerdo, o puede elegir una ubicación explícita usando el OPENSSL_CONF Variable ambiental).

Establezca las siguientes opciones (encuentre 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 (más bien que arreglarlo en un archivo de configuración) aquí: http://www.crsr.net/Notes/SSL.html