2009-11-18 56 views
29

我有一個Grizzly製作的REST服務器,它使用HTTPS並且可以和Firefox一起使用。下面的代碼:在Java中使用HTTPS和REST

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

現在,我試圖用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); 
} 

並獲得:

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) 

從例子,我已經在網絡上發現,它好像我需要設置一個Truststore然後設置某種TrustManager。這看起來像很多代碼和設置工作爲我的簡單的小項目。有沒有簡單的方法來說...我相信這個證書,並指向一個.cert文件?

回答

42

當您說「有更簡單的方法來......信任此證書」時,這正是您將Java證書添加到您的Java信任庫中所做的事情。這非常非常容易實現,並且在客戶端應用程序中無需執行任何操作即可使該信任庫得到識別或利用。

在您的客戶機上,找到您的cacerts文件是(這是默認的Java信任庫,並且是在默認情況下,位於< java主>/lib/security中/證書/ cacerts中。

然後,鍵入以下內容:

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

這將導入證書到您的信任存儲,並在此之後,您的客戶端應用程序將能夠連接到您的灰熊HTTPS服務器沒有問題

如果你不」。不想要重要將證書放入您的默認信任存儲庫中 - 即,您只希望它可供此一個客戶端應用程序使用,但不適用於您在該機器上的JVM上運行的任何其他應用程序 - 然後,您可以創建一個新的信任存儲庫你的應用。相反,通過密鑰工具的路徑存在,默認cacerts文件中,通過密鑰工具的路徑,新的信任存儲文件:

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

您會被要求設置並驗證了信任存儲文件的新密碼。然後,當您啓動您的客戶端應用程序時,請使用以下參數啓動它:

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

容易俗氣,真的。

+0

非常好,但...我可以在代碼中設置trustStore和trustStorePassword而不是VM的參數嗎? – User1 2009-11-18 19:41:08

+3

是 - 在調用SSL需要的方法之前,請使用System.setProperty(「javax.net.ssl.trustStore」,「<新建信任存儲庫的路徑>>」),並對javax.net.ssl執行相同的操作。 trustStorePassword。 – delfuego 2009-11-18 19:48:37

+1

一個令人難過的部分是,不正確的文件名會給出一個非常模糊的異常:「java.security.InvalidAlgorithmParameterException:trustAnchors參數必須爲非空」 – User1 2009-11-18 20:27:16

10

這裏的痛苦路線:

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

愛是愛的六種不同的捕獲的異常:)。有一些重構可以簡化代碼。但是,我喜歡虛擬機上的delfuego的-D選項。我希望有一個javax.net.ssl.trustStore靜態屬性,我可以設置。只需兩行代碼即可完成。任何人都知道那會是什麼?

這可能太多要問,但是,理想情況下keytool不會被使用。相反,trustedStore將由代碼動態創建,並且在運行時添加證書。

必須有更好的答案。

+0

投票贊成,因爲痛苦的路線是我尋找我的(稍微複雜的)項目! :) – 2010-05-18 17:00:03

+0

我們從哪裏得到這個文件? C:\\ truststore_client – 2014-12-31 17:30:07

+1

Java 7允許在一個塊中創建多個異常捕獲xD – 2016-09-05 21:54:07

3

需要記住的一點是,這個錯誤不僅是由於自簽名證書造成的。新的Entrust CA證書會以相同的錯誤失敗,並且正確的做法是使用適當的根證書更新服務器,而不是禁用此重要的安全功能。