2009-11-18 11 views
29

Ich habe einen REST-Server in Grizzly, der HTTPS verwendet und wunderbar mit Firefox funktioniert. Hier ist der Code:Verwendung von HTTPS mit 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(); 

Jetzt versuche ich es in Java zu erreichen:

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

Und erhalten:

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) 

Aus den Beispielen, die ich im Internet gefunden habe, es Es scheint, als müsste ich einen Truststore einrichten und dann eine Art TrustManager einrichten. Das scheint eine Menge Code und Setup-Arbeit für mein einfaches kleines Projekt zu sein. Gibt es einen einfacheren Weg, um einfach zu sagen .. Ich vertraue diesem Zertifikat und zeigen auf eine .cert-Datei?

Antwort

42

Wenn Sie sagen: "Gibt es einen einfacheren Weg, ... diesem Zertifikat zu vertrauen", dann tun Sie das, indem Sie das Zertifikat zu Ihrem Java Trust Store hinzufügen. Und das ist sehr, sehr einfach zu tun, und es gibt nichts, was Sie in Ihrer Client-App tun müssen, um diesen Vertrauensspeicher zu erkennen oder zu nutzen.

Auf dem Client-Rechner finden, wo Datei Ihre cacerts ist (dass Ihr Standard-Java Trust Store ist, und ist in der Standardeinstellung befindet sich auf < java-home >/lib/security/certs/cacerts.

Dann folgende, geben Sie:

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

, dass das Zertifikat in Ihrem Trust Store importiert, und danach wird Ihre Client-Anwendung in der Lage auf Ihre Grizzly HTTPS-Server ohne Probleme verbinden

Wenn Sie don‘. Ich möchte mich einprägen Wenn Sie das Zertifikat in Ihren Standard-Truststore aufnehmen möchten - dh Sie möchten nur, dass es für diese eine Client-App verfügbar ist, aber nicht für andere Anwendungen, die Sie auf Ihrer JVM auf diesem Computer ausführen - können Sie einen neuen Trust-Store erstellen deine App Statt vorbei keytool den Pfad zur bestehenden, default cacerts Datei, übergeben keytool den Pfad zu Ihrer neuen Trust Store-Datei:

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

werden Sie gefragt, zu setzen und ein neues Passwort für die Trust Store-Datei zu überprüfen. Starten Sie dann Ihre Client-App mit den folgenden Parametern:

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

Einfach kitschig, wirklich.

+0

Sehr schön, aber ... Kann ich die trustStore und trustStorePassword im Code statt Argumente an die VM setzen? – User1

+3

Ja - vor allen Aufrufen von SSL-erfordernden Methoden verwenden Sie System.setProperty ("javax.net.ssl.trustStore", ") und machen dasselbe für javax.net.ssl. trustStorePassword. – delfuego

+1

Ein trauriger Teil ist, dass ein falscher Dateiname eine sehr vage Ausnahme gibt: "java.security.InvalidAlgorithmParameterException: Der Parameter trustAnchors muss nicht leer sein" – User1

10

Hier ist die schmerzhafte Route:

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

Muß ich liebe diese sechs verschiedene gefangenen Ausnahmen :). Es gibt sicherlich einige Refactoring, um den Code ein wenig zu vereinfachen. Aber ich mag Delfuegos -D-Optionen auf der VM. Ich wünschte, es gäbe eine statische Eigenschaft javax.net.ssl.trustStore, die ich einfach einstellen könnte. Nur zwei Zeilen Code und fertig. Wer weiß, wo das wäre?

Dies ist vielleicht zu viel verlangt, aber im Idealfall würde das Keytool nicht verwendet werden. Stattdessen wird der trustedStore dynamisch vom Code erstellt und das Zertifikat wird zur Laufzeit hinzugefügt.

Es muss eine bessere Antwort geben.

+0

Wählen Sie dies, weil die schmerzhafte Route ist, was ich für mein (etwas komplexer) Projekt suchte! :) –

+0

Woher bekommen wir diese Datei? C: \\ truststore_client –

+1

Java 7 ermöglicht mehrere Ausnahmen in einem Block xD –

3

Etwas zu beachten ist, dass dieser Fehler nicht nur auf selbstsignierte Zertifikate zurückzuführen ist. Die neuen CAs der Entrust-Zertifizierungsstelle schlagen mit demselben Fehler fehl und es ist richtig, den Server mit den entsprechenden Root-Zertifikaten zu aktualisieren und diese wichtige Sicherheitsfunktion nicht zu deaktivieren.