2009-11-18 23 views
29

Ho un server REST realizzato in Grizzly che utilizza HTTPS e funziona meravigliosamente con Firefox. Ecco il codice:Utilizzo di HTTPS con REST in 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(); 

Ora, cerco di raggiungerlo in 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); 
} 

E ottenere:

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) 

Da esempi che ho trovato sul web, sembra che avrei bisogno di installare un Truststore, quindi installare una sorta di TrustManager. Questo sembra un sacco di codice e lavoro di installazione per il mio piccolo progetto. C'è un modo più semplice per dire solo ... Mi fido di questo certificato e punto a un file .cert?

risposta

42

Quando si dice "esiste un modo più semplice per ... fidarsi di questo certificato", questo è esattamente ciò che si sta facendo aggiungendo il certificato al proprio negozio di fiducia Java. E questo è molto, molto facile da fare, e non c'è nulla che devi fare all'interno della tua app client per ottenere il riconoscimento o l'utilizzo di tale fiduciario.

Sulla macchina client, trovare dove il file cacerts è (che è il tuo negozio di fiducia Java di default, ed è, per impostazione predefinita, che si trova a < java-casa >/lib/security/certs/cacerts.

Poi , digitare quanto segue:

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

che consente di importare il CERT nel tuo negozio di fiducia, e dopo questo, il vostro cliente applicazione sarà in grado di connettersi al server Grizzly HTTPS senza edizione

Se don'. Voglio impor t il cert nel tuo trust store di default - cioè, vuoi solo che sia disponibile per questa app di un client, ma non per qualsiasi altra cosa tu esegua sulla tua JVM su quella macchina - allora puoi creare un nuovo negozio di fiducia solo per la tua app. Invece di passare keytool il percorso del, file di cacerts di default esistente, passare keytool il percorso del nuovo file negozio di fiducia:

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

Ti verrà chiesto di impostare e verificare una nuova password per il file di archivio fiducia. Quindi, quando avvii la tua app client, avviarla con i seguenti parametri:

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

Facile, davvero.

+0

Molto bello, ma ... Posso impostare trustStore e trustStorePassword nel codice piuttosto che argomenti nella VM? – User1

+3

Sì: prima di tutte le chiamate a SSL che richiedono metodi, utilizzare System.setProperty ("javax.net.ssl.trustStore", ") e fare lo stesso per javax.net.ssl. trustStorePassword. – delfuego

+1

Una parte triste è che un nome file errato fornisce un'eccezione molto vaga: "java.security.InvalidAlgorithmParameterException: il parametro trustAnchors deve essere non vuoto" – User1

10

Ecco il percorso doloroso:

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

Gotta love quei sei diverse eccezioni travolte :). Ci sono certamente alcuni refactoring per semplificare un po 'il codice. Ma mi piacciono le opzioni -D del delfuego sulla VM. Vorrei che esistesse una proprietà statica javax.net.ssl.trustStore che avrei potuto semplicemente impostare. Solo due righe di codice e fatto. Qualcuno sa dove sarebbe?

Questo potrebbe essere troppo chiedere, ma, idealmente, il keytool non verrebbe utilizzato. Invece, il trustedStore verrebbe creato dinamicamente dal codice e il certificato verrà aggiunto in fase di runtime.

Ci deve essere una risposta migliore.

+0

Valutando questo perché la via dolorosa è ciò che stavo cercando per il mio progetto (leggermente più complesso)! :) –

+0

da dove viene ottenuto questo file? C: \\ truststore_client –

+1

Java 7 consente più catch di eccezioni in un blocco xD –

3

Qualcosa da tenere a mente è che questo errore non è dovuto solo ai certificati autofirmati. La nuova CA Entrust ha esito negativo con lo stesso errore e la cosa giusta da fare è aggiornare il server con i certificati root appropriati, non per disabilitare questa importante funzione di sicurezza.

Problemi correlati