2009-11-18 26 views
29

Tengo un servidor REST creado en Grizzly que usa HTTPS y funciona maravillosamente con Firefox. Aquí está el código:Uso de HTTPS con REST en Java

//Build a new Servlet Adapter. 
ServletAdapter adapter=new ServletAdapter(); 
adapter.addInitParameter("com.sun.jersey.config.property.packages", "My.services"); 
adapter.addInitParameter(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, SecurityFilter.class.getName()); 
adapter.setContextPath("/"); 
adapter.setServletInstance(new ServletContainer()); 

//Configure SSL (See instructions at the top of this file on how these files are generated.) 
SSLConfig ssl=new SSLConfig(); 
String keystoreFile=Main.class.getResource("resources/keystore_server.jks").toURI().getPath(); 
System.out.printf("Using keystore at: %s.",keystoreFile); 
ssl.setKeyStoreFile(keystoreFile); 
ssl.setKeyStorePass("asdfgh"); 

//Build the web server. 
GrizzlyWebServer webServer=new GrizzlyWebServer(getPort(9999),".",true); 

//Add the servlet. 
webServer.addGrizzlyAdapter(adapter, new String[]{"/"}); 

//Set SSL 
webServer.setSSLConfig(ssl); 

//Start it up. 
System.out.println(String.format("Jersey app started with WADL available at " 
    + "%sapplication.wadl\n", 
     "https://localhost:9999/")); 
webServer.start(); 

Ahora, trato de llegar a ella en Java:

SSLContext ctx=null; 
try { 
    ctx = SSLContext.getInstance("SSL"); 
} catch (NoSuchAlgorithmException e1) { 
    e1.printStackTrace(); 
} 
ClientConfig config=new DefaultClientConfig(); 
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(null,ctx)); 
WebResource service=Client.create(new DefaultClientConfig()).resource("https://localhost:9999/"); 

//Attempt to view the user's page. 
try{ 
    service 
     .path("user/"+username) 
     .get(String.class); 
} 

Y conseguir:

com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:128) 
at com.sun.jersey.api.client.Client.handle(Client.java:453) 
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:557) 
at com.sun.jersey.api.client.WebResource.get(WebResource.java:179) 

De ejemplos que he encontrado en la web, parece que tendría que configurar un Truststore y luego configurar algún tipo de TrustManager. Esto parece mucho trabajo de configuración y código para mi pequeño proyecto simple. ¿Hay alguna manera más fácil de decirlo ... Confío en este certificado y apunto a un archivo .cert?

Respuesta

42

Cuando dices "¿hay alguna manera más fácil de ... confiar en este certificado?", Eso es exactamente lo que estás haciendo al agregar el certificado a tu tienda de confianza de Java. Y esto es muy, muy fácil de hacer, y no hay nada que deba hacer en su aplicación cliente para que esa tienda de confianza sea reconocida o utilizada.

En la máquina cliente, saber dónde está su archivo cacerts es (ese es su defecto almacén de confianza de Java, y es, por defecto, que se encuentra en < java-hogar >// seguridad/certs/cacerts lib.

Entonces , escriba lo siguiente:

keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to cacerts> 

Eso va a importar el certificado en su almacén de confianza, y después de esto, la aplicación cliente será capaz de conectarse a su servidor Grizzly HTTPS sin descendencia

Si no'. Quiero Importar t el certificado en su tienda de confianza predeterminada, es decir, si solo desea que esté disponible para esta aplicación de cliente, pero no para nada más que ejecute en su JVM en esa máquina, entonces puede crear una nueva tienda de confianza solo para tu aplicación En lugar de pasar herramienta de claves de la ruta de acceso al, cacerts archivo por defecto existente, pasar herramienta de claves la ruta a su nuevo archivo de almacén de confianza:

keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to new trust store> 

Se le pedirá para establecer y verificar una nueva contraseña del archivo de almacén de confianza. Luego, cuando inicie su aplicación cliente, iníciela con los siguientes parámetros:

java -Djavax.net.ssl.trustStore=<path to new trust store> -Djavax.net.ssl.trustStorePassword=<trust store password> 

Easy cheesy, really.

+0

Muy bien, pero ... ¿Puedo establecer trustStore y trustStorePassword en el código en lugar de argumentos para la VM? – User1

+3

Sí, antes de cualquier llamada a métodos que requieran SSL, use System.setProperty ("javax.net.ssl.trustStore", ") y haga lo mismo con javax.net.ssl. trustStorePassword. – delfuego

+1

Una parte triste es que un nombre de archivo incorrecto ofrece una excepción muy vaga: "java.security.InvalidAlgorithmParameterException: el parámetro trustAnchors debe estar no vacío" – User1

10

Aquí está la ruta dolorosa:

SSLContext ctx = null; 
    try { 
     KeyStore trustStore; 
     trustStore = KeyStore.getInstance("JKS"); 
     trustStore.load(new FileInputStream("C:\\truststore_client"), 
       "asdfgh".toCharArray()); 
     TrustManagerFactory tmf = TrustManagerFactory 
       .getInstance("SunX509"); 
     tmf.init(trustStore); 
     ctx = SSLContext.getInstance("SSL"); 
     ctx.init(null, tmf.getTrustManagers(), null); 
    } catch (NoSuchAlgorithmException e1) { 
     e1.printStackTrace(); 
    } catch (KeyStoreException e) { 
     e.printStackTrace(); 
    } catch (CertificateException e) { 
     e.printStackTrace(); 
    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (KeyManagementException e) { 
     e.printStackTrace(); 
    } 
    ClientConfig config = new DefaultClientConfig(); 
    config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, 
      new HTTPSProperties(null, ctx)); 

    WebResource service = Client.create(config).resource(
      "https://localhost:9999/"); 
    service.addFilter(new HTTPBasicAuthFilter(username, password)); 

    // Attempt to view the user's page. 
    try { 
     service.path("user/" + username).get(String.class); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

Te va a encantar esos seis excepciones atrapado diferentes :). Ciertamente hay algunas refactorizaciones para simplificar un poco el código. Pero, me gustan las opciones -D de delfuego en la VM. Desearía que hubiera una propiedad estática de javax.net.ssl.trustStore que pudiera establecer. Solo dos líneas de código y listo. Alguien sabe dónde estaría eso?

Esto puede ser demasiado pedir, pero, idealmente, la herramienta de claves no se usaría. En cambio, la tienda trustedStore sería creada dinámicamente por el código y el cert se agregará en el tiempo de ejecución.

Debe haber una respuesta mejor.

+0

¡Votando esto porque la ruta dolorosa es lo que estaba buscando para mi proyecto (un poco más complejo)! :) –

+0

de donde obtenemos este archivo? C: \\ truststore_client –

+1

Java 7 permite varias capturas de excepción en un bloque xD –

3

Algo a tener en cuenta es que este error no se debe solo a certs autofirmados. Los nuevos certificados de Entrust CA fallan con el mismo error, y lo correcto es actualizar el servidor con los certificados de raíz apropiados, no para deshabilitar esta importante característica de seguridad.