Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Consuming RESTful service over https with certificate using Java

I'm a new user to REST services. I need to consume a RESTful API service generated with Jersey. The problem comes because that service is hosted on remote host and it requires https access with certificate.

I got my certificate from the organization and i'm able to access that REST API service with any of my browsers (with certificate set on them).

I've read lot of posts over here, and I've followed the answer on this topic: Using HTTPS with REST in Java

Now I've my certificate setup on my Java Keystore. But i don't know how to use that on my Java program so it uses exactly the certificate i need to do the https connection.

This is my simple connection code for local tests.

package rest.test.first;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

public class TestClient{
public static void main(String[]args){
    ClientConfig config= new DefaultClientConfig();
    Client client=Client.create(config);
    WebResource service=client.resource(getBaseURI());
    //Fluentinterfaces
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_PLAIN).get(ClientResponse.class).toString());
    //Getplaintext
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_PLAIN).get(String.class));
    //GetXML
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_XML).get(String.class));
    //TheHTML
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_HTML).get(String.class));
}

private static URI getBaseURI(){
    return UriBuilder.fromUri("http://localhost:8080/rest.test").build();
}
}

I'v read about using system set properties to specify path to keystore with this code:

System.setProperty("javax.net.ssl.keyStore", "/path/to/keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "password");

I still get 401 Error on connection to the remote server.

But then i don't know how to make SSL connection using my certificate on keystore. I've been also reading about using sslSocketFactory for this purpose but i couldn't make it work as explained on this post: How can I use different certificates on specific connections?

I've managed to retrieve my cert from keystore with this code.. now I just need to know how to use it in connection:

package rest.test.first;

    import java.io.FileInputStream;
    import java.security.KeyStore;
    import java.security.cert.Certificate;

    public class keystore {

      public static void main(String[] args) throws Exception {
        String keystoreFilename = "/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts";

        char[] password = "changeit".toCharArray();
        String alias = "remote_https_server";

        FileInputStream fIn = new FileInputStream(keystoreFilename);
        KeyStore keystore = KeyStore.getInstance("JKS");

        keystore.load(fIn, password);

        Certificate cert = keystore.getCertificate(alias);

        System.out.println(cert);
      }
    }

Ok that's the last script I've wrote. I can connect to https sites but i still can't connect to https sites which require to send my certificate to authenticate.

package rest.test.first;


import java.io.*;
import java.net.*;
import java.security.*;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;


public class urlConnection{
  public static void main(String args[]) throws Exception {

     System.setProperty("javax.net.ssl.trustStore", "/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts"); 
  System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
  Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

      //TrustStore..
      char[] passphrase = "changeit".toCharArray(); //password
      KeyStore keystore = KeyStore.getInstance("JKS");
      //KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(new FileInputStream("/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts"), passphrase); //path

      //TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); //instance
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      tmf.init(keystore);


      SSLContext context = SSLContext.getInstance("TLS");
      TrustManager[] trustManagers = tmf.getTrustManagers();
      context.init(null, trustManagers, null);
      SSLSocketFactory sf = context.getSocketFactory();
      URL url = new URL("https://www.google.es");

      HttpsURLConnection httpsCon = (HttpsURLConnection) url.openConnection();
      httpsCon.setSSLSocketFactory(sf);
      httpsCon.setRequestMethod("GET");

      /*InputStream inStrm = httpsCon.getInputStream();
      System.out.println("\nContent at " + url);
      int ch;
        while (((ch = inStrm.read()) != -1)){
          System.out.print((char) ch);
        inStrm.close();
      }*/

      System.out.println("Response Message is " + httpsCon.getResponseMessage());

 }
}
like image 941
gmarco Avatar asked Jul 11 '12 09:07

gmarco


People also ask

Can restful API be configured to use HTTPS?

You can enable HTTPS just for encryption, or you can also configure a REST API for client authentication (mutual authentication). Because REST APIs always use the integration server HTTP listener for the integration server, you must configure the integration server HTTP listener.


1 Answers

Assuming that you are deploying this code on a server and you have done Everything else correctly (Like generating keystore core correctly and placing it at a location where it can be accessed by your server ,using same java version as your code to generate the keystore )then i think what you need to do is add following

<Connector SSLEnabled="true" clientAuth="false" keystoreFile="pathToKeystore" keystorePass="password" maxThreads="150" port="443" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS"/>

in server.xml of your Server instance on which you are running the client and

<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"/>

IMP: If you are using Skype alongside this code be sure to (uncheck) change the default value as it also uses the same ports (80 and 443) for additional connections

like image 151
mayank vats Avatar answered Sep 28 '22 02:09

mayank vats