在 Java 中将 HTTPS 与 REST 一起使用

2022-09-01 06:38:58

我有一个在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();

现在,我尝试在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)

从我在网上找到的例子来看,似乎我需要设置一个信任库,然后设置某种TrustManager。对于我的简单小项目来说,这似乎需要大量的代码和设置工作。有没有更简单的方法来说..我是否信任此证书并指向 .cert 文件?


答案 1

当你说“有没有更简单的方法...信任此证书“,这正是通过将证书添加到 Java 信任存储区来执行的操作。这非常非常容易做到,并且无需在客户端应用程序中执行任何操作即可识别或利用该信任存储。

在客户端计算机上,找到 cacerts 文件所在的位置(这是默认的 Java 信任存储区,默认情况下位于 <java-home>/lib/security/certs/cacerts。

然后,键入以下内容:

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

这会将证书导入到您的信任存储中,在此之后,您的客户端应用程序将能够毫无问题地连接到您的Grizzly HTTPS服务器。

如果您不想将证书导入到默认信任存储中 - 即,您只希望它可用于此客户端应用程序,但不适用于您在该计算机上的JVM上运行的任何其他内容 - 那么您可以仅为您的应用程序创建一个新的信任存储。不要将 keytool 的路径传递到现有的缺省 cacerts 文件,而是将 keytool 的路径传递到新的信任库文件:

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>

很容易俗气,真的。


答案 2

这是痛苦的路线:

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

必须爱上:)这六个不同的例外。当然有一些重构来简化代码。但是,我喜欢 VM 上的 delfuego -D 选项。我希望有一个javax.net.ssl.trustStore静态属性,我可以设置它。只需两行代码即可完成。有人知道那会在哪里吗?

这可能要求太多了,但是,理想情况下不会使用keytool。相反,trustedStore 将由代码动态创建,并在运行时添加证书。

必须有一个更好的答案。