在 Java 中将 HTTPS 与 REST 结合使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1757295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using HTTPS with REST in Java
提问by User1
I have a REST server made in Grizzly that uses HTTPS and works wonderfully with Firefox. Here's the code:
我有一个用 Grizzly 制作的 REST 服务器,它使用 HTTPS 并与 Firefox 完美配合。这是代码:
//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();
Now, I try to reach it in Java:
现在,我尝试用 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);
}
And get:
并得到:
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)
From examples that I've found on the web, it seems like I would need to setup a Truststore then setup some sort of TrustManager. This seems like a lot of code and setup work for my simple little project. Is there an easier way to just say..I trust this cert and point to a .cert file?
从我在网上找到的例子来看,我似乎需要设置一个 Truststore,然后设置某种 TrustManager。对于我的简单小项目来说,这似乎是很多代码和设置工作。有没有更简单的方法可以说..我相信这个证书并指向一个 .cert 文件?
采纳答案by delfuego
When you say "is there an easier way to... trust this cert", that's exactly what you're doing by adding the cert to your Java trust store. And this is very, very easy to do, and there's nothing you need to do within your client app to get that trust store recognized or utilized.
当您说“是否有更简单的方法来……信任此证书”时,这正是您通过将证书添加到 Java 信任库所做的工作。这非常非常容易做到,您无需在客户端应用程序中执行任何操作即可识别或利用该信任库。
On your client machine, find where your cacerts file is (that's your default Java trust store, and is, by default, located at <java-home>/lib/security/certs/cacerts.
在您的客户端计算机上,找到您的 cacerts 文件所在的位置(这是您的默认 Java 信任存储,默认情况下位于 <java-home>/lib/security/certs/cacerts.conf )。
Then, type the following:
然后,键入以下内容:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to cacerts>
That will import the cert into your trust store, and after this, your client app will be able to connect to your Grizzly HTTPS server without issue.
这会将证书导入您的信任存储区,之后,您的客户端应用程序将能够毫无问题地连接到您的 Grizzly HTTPS 服务器。
If you don't want to import the cert into your default trust store -- i.e., you just want it to be available to this one client app, but not to anything else you run on your JVM on that machine -- then you can create a new trust store just for your app. Instead of passing keytool the path to the existing, default cacerts file, pass keytool the path to your new trust store file:
如果您不想将证书导入您的默认信任库——即,您只希望它可用于这个客户端应用程序,但不能用于您在那台机器上的 JVM 上运行的任何其他应用程序——那么您可以为您的应用创建一个新的信任存储。不是将 keytool 传递到现有的默认 cacerts 文件的路径,而是将 keytool 传递到新信任存储文件的路径:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to new trust store>
You'll be asked to set and verify a new password for the trust store file. Then, when you start your client app, start it with the following parameters:
系统会要求您为信任存储文件设置并验证新密码。然后,当您启动客户端应用程序时,请使用以下参数启动它:
java -Djavax.net.ssl.trustStore=<path to new trust store> -Djavax.net.ssl.trustStorePassword=<trust store password>
Easy cheesy, really.
容易俗气,真的。
回答by User1
Here's the painful 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();
}
Gotta love those six different caught exceptions :). There are certainly some refactoring to simplify the code a bit. But, I like delfuego's -D options on the VM. I wish there was a javax.net.ssl.trustStore static property that I could just set. Just two lines of code and done. Anyone know where that would be?
一定要喜欢这六个不同的捕获异常:)。当然有一些重构来简化代码。但是,我喜欢 VM 上的 delfuego 的 -D 选项。我希望有一个我可以设置的 javax.net.ssl.trustStore 静态属性。两行代码就搞定了。有谁知道那会是哪里?
This may be too much to ask, but, ideally the keytool would not be used. Instead, the trustedStore would be created dynamically by the code and the cert is added at runtime.
这可能问得太多,但理想情况下不会使用 keytool。相反,trustedStore 将由代码动态创建,并在运行时添加证书。
There must be a better answer.
一定有更好的答案。
回答by neel
Check this out: http://code.google.com/p/resting/. I could use resting to consume HTTPS REST services.
看看这个:http: //code.google.com/p/resting/。我可以使用 resting 来使用 HTTPS REST 服务。
回答by steve
Something to keep in mind is that this error isn't only due to self signed certs. The new Entrust CA certs fail with the same error, and the right thing to do is to update the server with the appropriate root certs, not to disable this important security feature.
需要记住的是,此错误不仅是由于自签名证书造成的。新的 Entrust CA 证书失败并出现相同的错误,正确的做法是使用适当的根证书更新服务器,而不是禁用这一重要的安全功能。