Apache HttpClient Component with Java

I’ll quickly note here how to embbed a certificate in a store, to be used by the Apache HttpClient component in Java.

This can be useful if you want to call a Web Service within your Java code, via HTTPS requests and you only have (for dev / test purposes) a self-signed certificate. It will in this case not be trusted by default, and you will get an exception telling you that it was unable to find a path for the certificate to match.

1- The certificate & the setup

Get the certificate you installed on your remote server (personnally I’ve made a PKCS12 one) and also get the password of the certificate.

Store it into your Java project.

Here is our raw example class :

public class MyApplication {

    private static final String USER_AGENT = "My Application";
    private static final String PATH = "https://localhost:8900/";

    private final Logger log = LoggerFactory.getLogger(MyApplication.class);

    private HttpClient client;
    private URL url;
    private KeyStore keyStore;
    private HttpsURLConnection urlConnection;

}

Then, in your class, build a Constructor which will correctly initialize your HttpClient :

public MyApplication() {
    this.url = new URL(PATH);

    String password = "yourpassword";
    this.keyStore = KeyStore.getInstance("pkcs12");

    File cert = new File("src/main/resources/server.p12");
    FileInputStream inputStream = null;
    if(cert.isFile())
            inputStream = new FileInputStream(cert);
    char[] pass = password.toCharArray();
    this.keyStore.load(inputStream, pass);
}

By doing this, you create a KeyStore, but your certificate file into it, associated with the password. Next step is to provide a TrustManager and to add it your KeyStore :

String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);

trustManagerFactory.init(this.keyStore);

To finish, you’ll just have to create a SSLContext object and a SSLConnectionSocket to create the client :

SSLContext sslContext = SSLContext.getInstance("TLS");

if (sslContext != null) {
        if (trustManagerFactory != null) {
            sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
        }
 }

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

The 2nd parameter of my SSLConnectionSocketFactory (ALLOW_ALL_HOSTNAME_VERIFIER) is only for test purposes, but when you have your certificate set up with the right hostname on the right server, you will not need it anymore. It will « bypass » the hostname match checker between cert and server. To test all that bunch on the localhost it may be required to put this option.

2- Make a call

Your HTTP Client is now ready to be used in a TLS context with your self-signed certificate, you will just have to write a query. Let’s take a POST request for our example, so write something like this in a class method :

public addCar(CarDTO car) {

    // do stuff
    String queryUrl = "api/car";
    HttpPost request = new HttpPost(this.url + queryUrl);
    request.addHeader("User-Agent", USER_AGENT);

    HttpResponse response = this.client.execute(request);
    String message = "";
    try {
        HttpEntity entity = response.getEntity();
        InputStream stream = entity.getContent();
        message = IOUtils.toString(stream);
        EntityUtils.consume(entity);
    } finally {
        return message;
    }
}

USER_AGENT is a constant string representing the tool name which make the query (for instance, when it’s a web browser, user-agent is the name of this browser !). If everything goes well, you should now be able to send requests via HTTPS on your third party server ! :)

Laisser un commentaire