Uso de HTTPS con REST en Java

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

//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, bash alcanzarlo 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 obten:

 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) 

A partir 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?

Cuando dices “¿hay alguna manera más fácil de … confiar en este certificado?”, Eso es exactamente lo que estás haciendo agregando 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 su máquina cliente, encuentre dónde está su archivo de cacerts (esa es su tienda de confianza Java predeterminada, y está, de manera predeterminada, ubicada en / lib / security / certs / cacerts.

Luego, escribe lo siguiente:

 keytool -import -alias  -file  -keystore  

Eso importará el certificado en su tienda de confianza, y después de esto, su aplicación de cliente podrá conectarse a su servidor Grizzly HTTPS sin problema.

Si no desea importar el certificado en su tienda de confianza predeterminada, es decir, si solo desea que esté disponible para esta aplicación cliente, pero no para otra cosa que ejecute en su JVM en esa máquina, entonces puede crea una nueva tienda de confianza solo para tu aplicación. En lugar de pasar keytool a la ruta al archivo cacerts predeterminado existente, pase keytool la ruta a su nuevo archivo de tienda de confianza:

 keytool -import -alias  -file  -keystore  

Se le pedirá que configure y verifique una nueva contraseña para el archivo del almacén de confianza. Luego, cuando inicie su aplicación cliente, comience con los siguientes parámetros:

 java -Djavax.net.ssl.trustStore= -Djavax.net.ssl.trustStorePassword= 

Fácil cursi, de verdad.

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(); } 

Tengo que amar esas seis excepciones atrapadas 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 mejor respuesta.

Algo a tener en cuenta es que este error no se debe solo a certs auto-firmados. 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.

Mira esto: http://code.google.com/p/resting/ . Podría usar reposo para consumir servicios REST HTTPS.