Skip to main content

How to use HTTPS with a client certificate in Java

·5 mins

There are several ways to establish an HTTP secure connection using a client certificate in Java. The simplest way to do so is by changing the default keystore in the corresponding environment variables at JVM start time.

As an example, we will use the following Class which performs an HTTPS connection and prints some details about the server certificate and the response:

import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import java.net.MalformedURLException;
import java.security.cert.Certificate;
import java.util.logging.Logger;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException;

public class HTTPSClient {

    private static String HTTPS_URL = "https://www.google.com";

    private final static Logger log = Logger.getLogger(
      HTTPSClient.class.getName());

    public static void main(String[] args) {
        new HTTPSClient().connect();
    }

    private void connect() {

        try {
            URL url = new URL(HTTPS_URL);
            HttpsURLConnection conn = (HttpsURLConnection)
            url.openConnection();
            conn.connect();
            Certificate[] certs = conn.getServerCertificates();

            if (conn != null) {
                for (Certificate cert : certs) {
                    log.info("Cert Type: " + cert.getType());
                    log.info("Cert Hash Code: " + cert.hashCode());
                    log.info("Cert Algorithm: "
                            + cert.getPublicKey().getAlgorithm());
                    log.info("Cert Format: " +
                    cert.getPublicKey().getFormat());
                }
            }

            BufferedReader br = new BufferedReader(
              new InputStreamReader(conn.getInputStream()));
            String line;

            while ((line = br.readLine()) != null) {
                log.info(line);
            }

            br.close();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (SSLPeerUnverifiedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

No we will build the source code:

javac HTTPSClient.java

As mentioned before, to perform an HTTPS connection with our client certificate we will need to changes the default JVM keystore by running the following command:

java -Djavax.net.ssl.keyStore=/home/user/certs/user.pfx \
-Djavax.net.ssl.keyStoreType=PKCS12 \
-Djavax.net.ssl.keyStorePassword=mypassword \
HTTPSClient

In case the HTTP server was configured with a self-signed certificate or the CA is not included in the default JVM truststore the connection will fail with a javax.net.SSLPeerUnverifiedException.

To fix this we will need to import the corresponding CA certificate into a new truststore and tell the JVM to use it.

We will use the keytool command included with the JVM to import the server’s CA certificate into the mycacerts truststore:

keytool -importcert -file /home/user/certs/myserver.pem \
-keystore /home/user/certs/mycacerts \
-storepass changeit -alias myserver

Now the command to run the JVM with the newly created truststore would be:

java -Djavax.net.ssl.keyStore=/home/user/certs/user.pfx \
-Djavax.net.ssl.keyStoreType=PKCS12 \
-Djavax.net.ssl.keyStorePassword=mypassword \
-Djavax.net.ssl.trustStore=/home/user/certs/mycacerts \
-Djavax.net.ssl.trustStorePassword=changeit \
-Djavax.net.ssl.trustStoreType=JKS \
HTTPSClient

The previous approach is fine to make some quick tests but we may need to use different truststores or client certificates in different parts of the code (imagine an application which is client of several HTTPS services and different client certificates are needed for each one)

The following is an example of java code demonstrating how to customize the keystore and truststore for a particular HTTP client only.

No need to mention that the harcoded configuration should be moved to a configuration file. This is left to the reader as an exercise.

import java.net.URL;

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

import java.net.MalformedURLException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.logging.Logger;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException;

public class HTTPSClient {

    private String HTTPS_URL = "https://www.google.com";
    private String KEY_STORE_FILE="/home/user/certs/user.pfx";
    private String KEY_STORE_PASS="mypassword";
    private String TRUST_STORE_FILE="/home/user/certs/mycacerts";
    private String TRUST_STORE_PASS="changeit";

    //Documented in security guides (Under "Standard Names") on both Java 7 VMs
    /** Oracle VM valid values
     *  - SunX509
     *  IBM VM valid values
     *  - IbmX509
     */
    private String KEY_MANAGER_ALGORITHM = "SunX509";
    /** Oracle VM valid values
     *  - PKCS12, JKS
     *  IBM VM valid values
     *  - PKCS12, JKS, JCEKS
     */
    private String KEY_STORE_FORMAT = "PKCS12";
    private String TRUST_STORE_FORMAT = "JKS";

    /** Oracle VM valid values
     *  - PKIX
     *  IBM VM valid values
     *  - PKIX or IbmPKIX or IbmX509
     */
    private String TRUST_MANAGER_ALGORITHM="PKIX";

    /** Oracle VM valid values
     *  - SSL SSLv2 SSLv3 TLS TLSv1 TLSv1.1 TLSv1.2
     *  IBM VM valid values
     *  - SSL SSLv3 TLS_SSL TLS TLSv1
     */
    // Oracle VM
    private String SSL_CONTEXT_ALGORITHM = "TLS";

    private final static Logger logger =
    Logger.getLogger(HTTPSClient.class.getName());

    public static void main(String[] args) {
        new HTTPSClient().connect();
    }

    private void connect() {

        try {
            URL url = new URL(HTTPS_URL);
            HttpsURLConnection conn = (HttpsURLConnection)
            url.openConnection();
            conn.setSSLSocketFactory(getFactory(new File(KEY_STORE_FILE),
              KEY_STORE_PASS, new File(TRUST_STORE_FILE), TRUST_STORE_PASS));
            conn.connect();
            Certificate[] certs = conn.getServerCertificates();

            if (conn != null) {
                for (Certificate cert : certs) {
                    logger.info("Cert Type: " + cert.getType());
                    logger.info("Cert Hash Code: " + cert.hashCode());
                    logger.info("Cert Algorithm: " +
                    cert.getPublicKey().getAlgorithm());
                    logger.info("Cert Format: " +
                    cert.getPublicKey().getFormat());
                }
            }

            BufferedReader br = new BufferedReader(new
            InputStreamReader(conn.getInputStream()));
            String line;

            while ((line=br.readLine()) != null) {
                logger.info (line);
            }

            br.close();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (SSLPeerUnverifiedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private SSLSocketFactory getFactory(File pKeyFile, String pKeyPassword, 
      File pTrustStoreFile, String pTrustStorePassword) {

        SSLSocketFactory socketFactory = null;

        try {

            KeyManagerFactory keyManagerFactory;
            keyManagerFactory =
            KeyManagerFactory.getInstance(KEY_MANAGER_ALGORITHM);
            KeyStore keyStore;
            keyStore = KeyStore.getInstance(KEY_STORE_FORMAT);
            InputStream keyInput = new FileInputStream(pKeyFile);
            keyStore.load(keyInput, pKeyPassword.toCharArray());
            keyInput.close();
            keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());

            TrustManagerFactory trustManagerFactory =
            TrustManagerFactory.getInstance(TRUST_MANAGER_ALGORITHM);
            KeyStore trustStore;
            trustStore = KeyStore.getInstance(TRUST_STORE_FORMAT);
            InputStream trustStoreInput = new FileInputStream(pTrustStoreFile);
            trustStore.load(trustStoreInput,
            pTrustStorePassword.toCharArray());
            trustStoreInput.close();
            trustManagerFactory.init(trustStore);

            SSLContext context = SSLContext.getInstance(SSL_CONTEXT_ALGORITHM);
            context.init(keyManagerFactory.getKeyManagers(),
            trustManagerFactory.getTrustManagers(), new SecureRandom());
            socketFactory=context.getSocketFactory();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return socketFactory;
    }
}