Using HTTPS with REST in Java

Viewed 112385

I have a REST server made in Grizzly that uses HTTPS and works wonderfully with Firefox. Here's the code:

//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:

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?

5 Answers

The answer of delfuego is the simplest way to solve the certificate problem. But, in my case, one of our third party url (using https), updated their certificate every 2 months automatically. It means that I have to import the cert to our Java trust store manually every 2 months as well. Sometimes it caused production problems.

So, I made a method to solve it with SecureRestClientTrustManager to be able to consume https url without importing the cert file. Here is the method:

     public static String doPostSecureWithHeader(String url, String body, Map headers)
            throws Exception {
        log.info("start doPostSecureWithHeader " + url + " with param " + body);
        long startTime;
        long endTime;
        startTime = System.currentTimeMillis();
        Client client;
        client = Client.create();
        WebResource webResource;
        webResource = null;
        String output = null;
        try{
            SSLContext sslContext = null;
            SecureRestClientTrustManager secureRestClientTrustManager = new SecureRestClientTrustManager();
            sslContext = SSLContext.getInstance("SSL");
            sslContext
            .init(null,
                    new javax.net.ssl.TrustManager[] { secureRestClientTrustManager },
                    null);
            DefaultClientConfig defaultClientConfig = new DefaultClientConfig();
            defaultClientConfig
            .getProperties()
            .put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
                    new com.sun.jersey.client.urlconnection.HTTPSProperties(
                            getHostnameVerifier(), sslContext));

            client = Client.create(defaultClientConfig);
            webResource = client.resource(url);

            if(headers!=null && headers.size()>0){
                for (Map.Entry entry : headers.entrySet()){
                    webResource.setProperty(entry.getKey(), entry.getValue());
                }
            }
            WebResource.Builder builder = 
                    webResource.accept("application/json");
            if(headers!=null && headers.size()>0){
                for (Map.Entry entry : headers.entrySet()){
                    builder.header(entry.getKey(), entry.getValue());
                }
            }

            ClientResponse response = builder
                    .post(ClientResponse.class, body);
            output = response.getEntity(String.class);
        }
        catch(Exception e){
            log.error(e.getMessage(),e);
            if(e.toString().contains("One or more of query value parameters are null")){
                output="-1";
            }
            if(e.toString().contains("401 Unauthorized")){
                throw e;
            }
        }
        finally {
            if (client!= null) {
                client.destroy();
            }
        }
        endTime = System.currentTimeMillis();
        log.info("time hit "+ url +" selama "+ (endTime - startTime) + " milliseconds dengan output = "+output);
        return output;
    }
Related