Search code examples
javahttpscertificatekeystore

How do I use client certificates in a client java application?


This is a topic that has taken me quite some time to figure out. There are bits and pieces of information scattered and one has to put everything together. I was hoping that with this post I could help others quickly assemble a working solution.


I have a client-cert.pem, client-key.pem and a root.pem files and I need to used them in my Java client to access a remote REST API.

How do I package them into a truststore and use them to make API calls?


Solution

  • In order to load your certificates into your application your will need to package them into a truststore.

    Creating a truststore

    given the 3 files:

    • client-cert.pem
    • client-key.pem
    • root.pem

    Run the following commands in your terminal. Replace PASSWORD with your desired password.

    1. Package your client key and certificate into a keystore. This will create a PKCS12 keystore file.

      openssl pkcs12 -export \
          -inkey client-key.pem -in client-cert.pem \
          -out client.pfx -passout pass:PASSWORD \
          -name qlikClient
      
    2. Add the keystore to your truststore. It will create a truststore if the destination doesn't exit. This will create a PKCS12 truststore file. By default it creates a JKS file which is a proprietary format. By specifying -deststoretype PKCS12 you will create a file which is in an industry standard format.

      keytool -importkeystore \
          -destkeystore truststore.pfx -deststoretype PKCS12 -deststorepass PASSWORD \
          -srckeystore client.pfx -srcstorepass PASSWORD -srcstoretype PKCS12 \
          -alias qlikClient
      
    3. Add your root CA to the truststore

      keytool -importcert \
          -keystore truststore.pfx -storepass PASSWORD \
          -file root.pem -noprompt \
          -alias qlikServerCACert
      

    Note that in the above commands we use the same PASSWORD for both the keystore and the truststore. You could alternatively use different passwords. Also note that you have to specify an alias for each item you add to the truststore.

    If you want your truststore to trust all cacerts available in your system add -trustcacerts option to step 2 or 3.

    You can use the following command to list the contents of your truststore

    keytool -list -keystore truststore.pfx -storepass PASSWORD
    

    Using the truststore in you application

    Once you have your truststore you need to load it into your application. Assuming you have a constant KEYSTORE_PATH holding the path to your truststore and keyStorePass holding the password, read the truststore file into a KeyStore

    private KeyStore readStore() {
      try (InputStream keyStoreStream = new FileInputStream(KEYSTORE_PATH)) {
        KeyStore keyStore = KeyStore.getInstance("PKCS12"); // or "JKS"
        keyStore.load(keyStoreStream, keyStorePass.toCharArray());
        return keyStore;
      } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
      }
    }
    

    Create a custom SSLContext and a custom HttpClient,

    final KeyStore truststore = readStore();
    
    final SSLContext sslContext;
    try {
      sslContext = SSLContexts.custom()
          .loadTrustMaterial(truststore, new TrustAllStrategy())
          .loadKeyMaterial(truststore, keyStorePass.toCharArray(), (aliases, socket) -> "qlikClient")
          .build();
    } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException e) {
      throw new RuntimeException("Failed to read keystore", e);
    }
    final CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
    

    You can now use this HttpClient to make requests to your API.

    HttpResponse response = httpClient.execute(new HttpGet("https://sense-gcp-central1eu.net:4242/qrs/app/full"));
    

    Or, if you are using the OpenUnirest/unirest-java library, you can configure Unirest to use your custom HttpClient

    Unirest.config().httpClient(httpClient);
    HttpResponse<JsonNode> response = Unirest.get("https://sense-gcp-central1eu.net:4242/qrs/app/full").asJson();
    

    References