2012-05-03 17 views
16

Hay algún código en nuestro sistema para generar automáticamente certificados autofirmados en un almacén de claves que luego Jetty utiliza. Si una clave para un equipo dado que ya existe, entonces no pasa nada, pero si no existe, se genera una nueva clave, como esto:Si hay más de un certificado en el almacén de claves de Jetty, ¿cómo elige?

public void generateKey(String commonName) { 
    X500Name x500Name = new X500Name("CN=" + commonName); 
    CertAndKeyGen keyPair = new CertAndKeyGen("DSA", "SHA1withDSA"); 
    keyPair.generate(1024); 
    PrivateKey privateKey = keyPair.getPrivateKey(); 
    X509Certificate certificate = keyPair.getSelfCertificate(x500Name, 20*365*24*60*60); 
    Certificate[] chain = { certificate }; 
    keyStore.setEntry(commonName, privateKey, "secret".toCharArray(), chain); 
} 

Todo esto funciona bien siempre y cuando sólo hay una clave y el certificado en la tienda de llaves Una vez que tenga varias claves, ocurren cosas extrañas cuando intenta conectarse:

java.io.IOException: HTTPS hostname wrong: should be <127.0.0.1> 

Esto fue un error bastante desconcertante pero finalmente lograron localizarlo escribiendo una prueba de unidad que se conecta al servidor y afirma que el CN en el certificado coincide con el nombre de host. Lo que encontré fue bastante interesante: Jetty parece elegir arbitrariamente qué certificado presentar al cliente, pero de manera consistente.

Por ejemplo:

  • Si "CN = localhost" y "CN = cheese.mydomain" están en el almacén de claves, siempre eligieron "CN = cheese.mydomain".
  • Si "CN = 127.0.0.1" y "CN = cheese.mydomain" están en el almacén de claves, siempre eligió "CN = cheese.mydomain".
  • Si "CN = 192.168.222.100" (cheese.mydomain) y "CN = cheese.mydomain" están en el almacén de claves, siempre eligió "CN = 192.168.222.100".

Escribí un código que recorre los certificados en la tienda para imprimirlos y descubrí que no siempre está eligiendo el primer certificado o algo trivial como ese.

¿Qué criterios exactamente utiliza? Inicialmente pensé que localhost era especial, pero luego el tercer ejemplo me desconcertó por completo.

Supongo que de alguna manera esto es decidido por KeyManagerFactory, que es SunX509 en mi caso.

Respuesta

13

Esto es, en efecto, finalmente decidido por el KeyManager (generalmente obtenido de un KeyManagerFactory).

Un almacén de claves puede tener una cantidad de certificados almacenados bajo diferentes alias. Si no se configura ningún alias explícitamente en certAlias in the Jetty configuration, la implementación SunX509 elegirá los primeros alias que encuentre para los que haya una clave privada y una clave del tipo de cifrado elegido (normalmente RSA, pero probablemente DSA en su caso aquí) . Hay un poco más de lógica de elección si miras el Sun provider implementation, pero no deberías realmente confiar en el orden en general, solo el nombre del alias.

Por supuesto, puede darle a Jetty su propio SSLContext con su propio X509KeyManager para elegir el alias. Usted tendría que poner en práctica:

chooseServerAlias(String keyType, Principal[] issuers, Socket socket) 

Por desgracia, aparte de keyType y issuers, lo único que consigue a tomar la decisión es la misma socket. En el mejor de los casos, la información útil que obtiene es la dirección IP local y la remota.

A menos que su servidor esté escuchando múltiples direcciones IP en el mismo puerto, siempre obtendrá la misma dirección IP local.(Aquí, obviamente, tiene al menos dos: 127.0.0.1 y 192.168.222.100, pero sospecho que no está realmente interesado en el servidor local, excepto en sus propias pruebas). Necesitaría el soporte de Indicación de nombre de servidor (SNI) en el servidor para poder para tomar una decisión basada en los nombres de host solicitados (por los clientes que la admiten). Desafortunadamente, SNI was only introduced in Java 7, but only on the client side.

Otro problema que usted enfrentará aquí es que Java clients will complain about IP addresses in the Subject DN's CN. Algunos navegadores tolerarían esto, pero esto no es compatible con la especificación HTTPS (RFC 2818). Las direcciones IP deben ser entradas de nombre alternativo del sujeto del tipo de dirección IP.

+0

Sí, también descubrimos lo mismo con las direcciones IP, lo que resultó en la escritura de nuestro propio HostnameVerifier para verificar si eso coincide y devuelve verdadero. Originalmente teníamos un HostnameVerifier que siempre devolvía verdadero, que simplemente tenía que descartarse por razones obvias. Muchos usuarios existentes configuraron una dirección IP como su "nombre de host" y no queremos atacar agresivamente eso por el momento. – Trejkaz

Cuestiones relacionadas