sslcontext-kickstart-for-apache

High level library to configure a SSLContext and other properties to enable SSL/TLS connection

License

License

Categories

Categories

SSLContext-Kickstart Security
GroupId

GroupId

io.github.hakky54
ArtifactId

ArtifactId

sslcontext-kickstart-for-apache
Last Version

Last Version

5.4.0
Release Date

Release Date

Type

Type

jar
Description

Description

sslcontext-kickstart-for-apache
High level library to configure a SSLContext and other properties to enable SSL/TLS connection
Project URL

Project URL

https://sslcontext-kickstart.com/

Download sslcontext-kickstart-for-apache

How to add to project

<!-- https://jarcasting.com/artifacts/io.github.hakky54/sslcontext-kickstart-for-apache/ -->
<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-apache</artifactId>
    <version>5.4.0</version>
</dependency>
// https://jarcasting.com/artifacts/io.github.hakky54/sslcontext-kickstart-for-apache/
implementation 'io.github.hakky54:sslcontext-kickstart-for-apache:5.4.0'
// https://jarcasting.com/artifacts/io.github.hakky54/sslcontext-kickstart-for-apache/
implementation ("io.github.hakky54:sslcontext-kickstart-for-apache:5.4.0")
'io.github.hakky54:sslcontext-kickstart-for-apache:jar:5.4.0'
<dependency org="io.github.hakky54" name="sslcontext-kickstart-for-apache" rev="5.4.0">
  <artifact name="sslcontext-kickstart-for-apache" type="jar" />
</dependency>
@Grapes(
@Grab(group='io.github.hakky54', module='sslcontext-kickstart-for-apache', version='5.4.0')
)
libraryDependencies += "io.github.hakky54" % "sslcontext-kickstart-for-apache" % "5.4.0"
[io.github.hakky54/sslcontext-kickstart-for-apache "5.4.0"]

Dependencies

compile (2)

Group / Artifact Type Version
io.github.hakky54 : sslcontext-kickstart jar 5.4.0
org.apache.httpcomponents : httpclient jar 4.5.13

test (4)

Group / Artifact Type Version
org.junit.jupiter : junit-jupiter-api jar 5.7.0
org.junit.jupiter : junit-jupiter-engine jar 5.7.0
org.assertj : assertj-core jar 3.18.1
io.github.hakky54 : logcaptor jar 2.2.0

Project Modules

There are no modules declared in this project.

Actions Status Security Rating Known Vulnerabilities Coverage Apache2 license Maven Central javadoc Join the chat at https://gitter.im/hakky54/sslcontext-kickstart

SonarCloud

SSLContext Kickstart Tweet

Install library with:

Install with Maven

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>6.0.0</version>
</dependency>

Install with Gradle

implementation 'io.github.hakky54:sslcontext-kickstart:6.0.0'

Install with Scala SBT

libraryDependencies += "io.github.hakky54" % "sslcontext-kickstart" % "6.0.0"

Install with Apache Ivy

<dependency org="io.github.hakky54" name="sslcontext-kickstart" rev="6.0.0" />

Table of contents

  1. Introduction
  2. Usage
  3. Additional mappers for specific libraries
  4. Tested HTTP Clients
  5. Contributing

Introduction

SSLContext Kickstart is a library which provides a High-Level SSLFactory class for configuring a http client to communicate over SSL/TLS for one way authentication or two-way authentication.

History

As a Java developer I worked for different kinds of clients. Most of the time the application required to call other microservices within the organization or some other http servers. These requests needed to be secured and therefore it was required to load the ssl materials into the http client. Each client may require different input value to enable https requests and therefore I couldn't just copy-paste my earlier configuration into the new project. The resulting configuration was in my opinion always verbose, not reusable, hard to test and hard to maintain.

As a developer you also need to know how to properly load your file into your application and consume it as a KeyStore instance. Therefore, you also need to understand how to properly create for example a KeyManager and a TrustManager for you SSLContext. An alternative for the traditional creation of SSLContext can be simplified if you use a Http Client which relies on libraries of Jetty, Netty or Apache. If you use other clients than you are out of luck. The sslcontext-kickstart library is taking the responsibility of creating an instance of SSLContext from the provided arguments, and it will provide you all the ssl materials which are required to configure 40+ different Http Client for Java, Scala and Kotlin. I wanted the library to be as easy as possible to use for all developers to give them a kickstart when configuring their Http Client. So feel free to provide feedback or feature requests. The library also provides other utilities such as:

See the javadoc for all the options.

Acknowledgement

I would like to thank Cody A. Ray for his contribution to the community regarding loading multiple Keystores into the SSLContext. The limitation of the JDK is to only support one keystore for the KeyManagerFactory and only one keystore for the TrustManagerFactory. The code snippets which Cody has shared are now available within this library and can be found here: CompositeX509KeyManager and CompositeX509TrustManager

The original content can be found here:

Advantages:

  • No need for low-level SSLContext configuration anymore
  • No knowledge needed about SSLContext, TrustManager, TrustManagerFactory, KeyManager, KeyManagerFactory and how to create it.
  • Above classes will all be created with just providing an identity and a trustStore
  • Load multiple identities/trustStores/keyManagers/trustManagers

Definitions

  • Identity: A KeyStore which holds the key pair also known as private and public key
  • TrustStore: A KeyStore containing one or more certificates also known as public key. This KeyStore contains a list of trusted certificates
  • One way authentication (also known as one way tls, one way ssl): Https connection where the client validates the certificate of the counter party
  • Two way authentication (also known as two way tls, two way ssl, mutual authentication): Https connection where the client as well as the counter party validates the certificate, also known as mutual authentication

Usage

Basic example configuration

Example configuration with apache http client, or click here to view the other client configurations

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

import nl.altindag.ssl.SSLFactory;

public class App {

    public static void main(String[] args) throws IOException, JSONException {
        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .build();

        HttpClient httpClient = HttpClients.custom()
                .setSSLContext(sslFactory.getSslContext())
                .setSSLHostnameVerifier(sslFactory.getHostnameVerifier())
                .build();

        HttpGet request = new HttpGet("https://api.chucknorris.io/jokes/random");

        HttpResponse response = httpClient.execute(request);
        String chuckNorrisJoke = new JSONObject(EntityUtils.toString(response.getEntity())).getString("value");

        System.out.println(String.format("Received the following status code: %d", response.getStatusLine().getStatusCode()));
        System.out.println(String.format("Received the following joke: %s", chuckNorrisJoke));
    }

}

Response:

Received the following status code: 200
Received the following joke: If a black cat crosses your path, you have bad luck. If Chuck Norris crosses your path, it was nice knowing you.

Other possible configurations

Loading keystore and truststore from the classpath
SSLFactory.builder()
          .withIdentityMaterial("identity.jks", "password".toCharArray())
          .withTrustMaterial("truststore.jks", "password".toCharArray())
          .build();
Loading keystore and trust store from anywhere on the filesystem
SSLFactory.builder()
          .withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "password".toCharArray())
          .withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "password".toCharArray())
          .build();
Trusting all certificates without validation, not recommended to use at production!
SSLFactory.builder()
          .withTrustingAllCertificatesWithoutValidation()
          .build();
Loading JDK and OS trusted certificates
SSLFactory.builder()
          .withDefaultTrustMaterial()
          .withSystemTrustMaterial()
          .build();
Using specific protocols, ciphers with custom secure random and hostname verifier

If you are using java 11 or newer, than you are also able to use TLSv1.3 as encryption protocol by default.

SSLFactory.builder()
          .withDefaultTrustMaterial()
          .withProtocols("TLSv1.3", "TLSv1.2")
          .withCiphers("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384")
          .withHostnameVerifier(hostnameVerifier)
          .withSecureRandom(secureRandom)
          .build();
Support for using multiple identity materials and trust materials
SSLFactory.builder()
          .withIdentityMaterial("identity-1.jks", password)
          .withIdentityMaterial("identity-2.jks", password)
          .withIdentityMaterial("identity-3.jks", password)
          .withIdentityMaterial("identity-4.jks", password)
          .withTrustMaterial("truststore-1.jks", password)
          .withTrustMaterial("truststore-2.jks", password)
          .withTrustMaterial("truststore-3.jks", password)
          .withTrustMaterial("truststore-4.jks", password)
          .build();
Support for using X509ExtendedKeyManager and X509ExtendedTrustManager
X509ExtendedKeyManager keyManager = ...
X509ExtendedTrustManager trustManager = ...

SSLFactory.builder()
          .withIdentityMaterial(keyManager)
          .withTrustMaterial(trustManager)
          .build();
Support for using PrivateKey and Certificates
PrivateKey privateKey = ...
char[] privateKeyPassword = ...
Certificate[] certificateChain = ...

Certificate trustedCertificate = ...

SSLFactory.builder()
          .withIdentityMaterial(privateKey, privateKeyPassword, certificateChain)
          .withTrustMaterial(trustedCertificate)
          .build();
Using PEM Files

Support for using pem formatted private key and certificates from classpath, any directory or as an InputStream. See PemUtilsShould for detailed usages. Add the dependency below to use this feature, it also includes the core features from the library such as SSLFactory.

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-pem</artifactId>
    <version>6.0.0</version>
</dependency>
 * EXAMPLE FILES
 *
 * [some-trusted-certificate.pem]
 * -----BEGIN CERTIFICATE-----
 *             ...
 *             ...
 * -----END CERTIFICATE-----
 *
 * [private-key.pem]
 * -----BEGIN PRIVATE KEY-----
 *             ...
 *             ...
 * -----END PRIVATE KEY-----
 *
 * [private-key.pem]
 * -----BEGIN RSA PRIVATE KEY-----
 *             ...
 *             ...
 * -----END RSA PRIVATE KEY-----
 *
 * [private-key.pem]
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 *             ...
 *             ...
 * -----END ENCRYPTED PRIVATE KEY-----
 */

Example usage:

X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial("certificate.pem", "private-key.pem");
X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");

SSLFactory.builder()
          .withIdentityMaterial(keyManager)
          .withTrustMaterial(trustManager)
          .build();

Returnable values from the SSLFactory

The SSLFactory provides different kinds of returnable values, see below for all the options:

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.model.KeyStoreHolder;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Optional;

public class App {

    public static void main(String[] args) {
        SSLFactory sslFactory = SSLFactory.builder()
                .withIdentityMaterial("keystore.p12", "secret".toCharArray(), "PKCS12")
                .withTrustMaterial("truststore.p12", "secret".toCharArray(), "PKCS12")
                .build();

        SSLContext sslContext = sslFactory.getSslContext();
        HostnameVerifier hostnameVerifier = sslFactory.getHostnameVerifier();
        Optional<X509ExtendedKeyManager> keyManager = sslFactory.getKeyManager();
        Optional<X509ExtendedTrustManager> trustManager = sslFactory.getTrustManager();
        List<X509Certificate> trustedCertificates = sslFactory.getTrustedCertificates();
        List<KeyStoreHolder> identities = sslFactory.getIdentities();
        List<KeyStoreHolder> trustStores = sslFactory.getTrustStores();
        SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
        SSLServerSocketFactory sslServerSocketFactory = sslFactory.getSslServerSocketFactory();
        SSLParameters sslParameters = sslFactory.getSslParameters();
        List<String> ciphers = sslFactory.getCiphers();
        List<String> protocols = sslFactory.getProtocols();
    }

}

Additional mappers for specific libraries

Some http clients relay on different ssl classes from third parties and require mapping from SSLFactory to those libraries. Below you will find the maven dependency which will provide the mapping and also the SSLFactory library. When using one of the below libraries, it is not required to also explicitly include sslcontext-kickstart.

Netty

Some know http clients which relay on netty libraries are: Spring WebFlux WebClient Netty, Async Http Client and Dispatch Reboot Http Client.

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-netty</artifactId>
    <version>6.0.0</version>
</dependency>

Example setup for Spring WebClient with Netty:

import io.netty.handler.ssl.SslContext;
import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.NettySslUtils;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;

import javax.net.ssl.SSLException;

public class App {
    
    public static void main(String[] args) throws SSLException {
        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .build();

        SslContext sslContext = NettySslUtils.forClient(sslFactory).build();
        HttpClient httpClient = HttpClient.create()
                .secure(sslSpec -> sslSpec.sslContext(sslContext));

        WebClient webClient = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();
    }

}

Jetty

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-jetty</artifactId>
    <version>6.0.0</version>
</dependency>

Example setup for Spring WebFlux WebClient Jetty:

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.JettySslUtils;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.http.client.reactive.JettyClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;

public class App {

    public static void main(String[] args) {
        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .build();
        
        SslContextFactory.Client sslContextFactory = JettySslUtils.forClient(sslFactory);
        HttpClient httpClient = new HttpClient(sslContextFactory);

        WebClient webClient = WebClient.builder()
                .clientConnector(new JettyClientHttpConnector(httpClient))
                .build();
    }

}

Apache

Apache 4

Apache Http Client works with javax.net.ssl.SSLContext, so an additional mapping to their library is not required, see here. However it is still possible to configure the http client with their custom configuration class. you can find below an example configuration for that use case:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-apache4</artifactId>
    <version>6.0.0</version>
</dependency>
import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.Apache4SslUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;

public class App {

    public static void main(String[] args) {
        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .build();

        LayeredConnectionSocketFactory socketFactory = Apache4SslUtils.toSocketFactory(sslFactory);

        HttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(socketFactory)
                .build();
    }

}
Apache 5
<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart-for-apache5</artifactId>
    <version>6.0.0</version>
</dependency>
import nl.altindag.ssl.SSLFactory;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
import nl.altindag.ssl.util.Apache5SslUtils;

class App {

    public static void main(String[] args) {
        SSLFactory sslFactory = SSLFactory.builder()
                .withDefaultTrustMaterial()
                .build();

        LayeredConnectionSocketFactory socketFactory = Apache5SslUtils.toSocketFactory(sslFactory);
        PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                .setSSLSocketFactory(socketFactory)
                .build();

        HttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .build();

        PoolingAsyncClientConnectionManager asyncConnectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
                .setTlsStrategy(Apache5SslUtils.toTlsStrategy(sslFactory))
                .build();

        CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom()
                .setConnectionManager(asyncConnectionManager)
                .build();
    }
    
}

Tested HTTP Clients

Below is a list of clients which have already been tested with examples, see in the ClientConfig class and the service directory for detailed configuration

Java

Kotlin

Scala

There is a github project available named Mutual-tls-ssl which provides a tutorial containing steps for setting up these four scenarios:

  • No security
  • One way authentication
  • Two way authentication
  • Two way authentication with trusting the Certificate Authority

It will also explain how to create KeyStores, Certificates, Certificate Signing Requests and how to implement it.

Contributing

There are plenty of ways to contribute to this project:

  • Give it a star
  • Join the Gitter room and leave a feedback or help with answering users questions
  • Submit a PR

Versions

Version
5.4.0
5.3.0
5.2.4
5.2.3
5.2.2
5.2.1
5.2.0
5.1.0
5.0.1
5.0.0
4.1.0
4.0.0
3.1.2
3.1.1
3.1.0
3.0.9
3.0.8
3.0.7
3.0.6