DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Mastering Unit Testing and Test-Driven Development in Java
  • Comprehensive Guide to Unit Testing Spring AOP Aspects
  • Improving Java Code Security
  • Hints for Unit Testing With AssertJ

Trending

  • A 5-Step SOC Guide That Meets RBI Expectations and Strengthens Security Operations
  • The ORM Is Over: AI-Written SQL Is the New Data Access Layer
  • Evaluating SOC Effectiveness Using Detection Coverage and Response Metrics
  • Throughput vs Goodput: The Performance Metric You Are Probably Ignoring in LLM Testing
  1. DZone
  2. Coding
  3. Java
  4. How to Configure SSL/TLS for a HTTP Client or a Server

How to Configure SSL/TLS for a HTTP Client or a Server

We discuss how to easily configure an SSL/TLS connection in Java to secure your application better. This setup can also easily be used for Scala and Kotlin.

By 
Hakan Altındağ user avatar
Hakan Altındağ
·
Updated Jul. 16, 21 · Tutorial
Likes (19)
Comment
Save
Tweet
Share
65.9K Views

Join the DZone community and get the full member experience.

Join For Free

Setting up encryption for your application, how hard can it be? I thought it would be easy. After all, all communication with modern web applications should be encrypted, right? Well, my expectations were wrong. While setting it up, I encountered a couple of hidden difficulties. For example, the configuration is vague, verbose, not straight-forward to set it up, hard to debug, and not unit-test friendly.

For this article, I'll assume you already have a basic understanding of certificates, keystores, encryption protocols, and ssl-handshake. If not, I would recommend going through this article: How to Easily Set Up Mutual TLS.

It will go through the following topics:

  • 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, and Certificate Signing Requests and how to implement these techniques.

Vanilla Java SSL Configuration

I want to provide a couple of examples to explain the hidden difficulties when setting up a secure connection with HTTPS and certificates in vanilla Java. For this example, I will use Apache HttpClient with mutual authentication.

Java
 
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.impl.client.HttpClients;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.KeyManagerFactory;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

public class App {

    public static void main(String[] args) {
        try {
            String keyStoreType = KeyStore.getDefaultType();
            Path trustStorePath = Paths.get("/path/to/truststore.jks");
            InputStream trustStoreStream = Files.newInputStream(trustStorePath, StandardOpenOption.READ);
            KeyStore trustStore = KeyStore.getInstance(keyStoreType);
            trustStore.load(trustStoreStream, "password".toCharArray());
            String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(trustManagerFactoryAlgorithm);
            trustManagerFactory.init(trustStore);
            trustStoreStream.close();

            Path identityPath = Paths.get("/path/to/identity.jks");
            InputStream identityStream = Files.newInputStream(identityPath, StandardOpenOption.READ);
            KeyStore identity = KeyStore.getInstance(keyStoreType);
            identity.load(identityStream, "password".toCharArray());
            String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
            keyManagerFactory.init(identity, "password".toCharArray());
            identityStream.close();

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(
                keyManagerFactory.getKeyManagers(), 
                trustManagerFactory.getTrustManagers(), 
                null
            );

            HttpClient httpClient = HttpClients.custom()
                    .setSSLContext(sslContext)
                    .setSSLHostnameVerifier(new DefaultHostnameVerifier())
                    .build();

            HttpGet request = new HttpGet("https://localhost:8443/api/hello");
            HttpResponse response = httpClient.execute(request);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
}


As you can see, this is really verbose, but this is a common code snippet that is being used when setting up SSL/TLS for an HTTP client. It is also hard to unit test an SSLContext object because you can't get any information from it that will tell if the trust manager is really initialized well and if it contains all the trusted certificates. The only way to really validate your code is by writing an integration test, where the client actually sends a request to a real server with HTTPS enabled.

Some other HTTP clients even require a different setup (e.g., Netty HttpClient, AsyncHttpClient, and Dispatch Reboot). These clients only accept an SSLContext from Netty's library instead of the one from the JDK.

I faced the same challenges as my colleagues. They also didn't enjoy setting it up. It was just a configuration that needed to be set up once, well enough to do the job, and after that, we were scared to touch it again. And it got even tougher when we wanted to use multiple keystores as trust material, for example.

I wanted to help myself out from the verbosity to make my life easier. One library to configure them all! It should be painless to use, easy to test and debug, and fun to set it up. The project is available at GitHub - SSLContext-Kickstart.

Install the Library

Add the library with one of the snippets below.

Maven:

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


Or for Gradle:

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


Or for Scala SBT:

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


Or for Apache Ivy:

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


Simplified SSL Configuration

Let's refactor the initial code snippet while using the library.

Java
 
import nl.altindag.ssl.SSLFactory;
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 java.nio.file.Paths;

public class App {

    public static void main(String[] args) throws Exception {
        SSLFactory sslFactory = SSLFactory.builder()
                .withIdentityMaterial(Paths.get("/path/to/identity.jks"), "password".toCharArray())
                .withTrustMaterial(Paths.get("/path/to/truststore.jks"), "password".toCharArray())
                .build();

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

        HttpGet request = new HttpGet("https://localhost:8443/api/hello");
        HttpResponse response = httpClient.execute(request);
    }

}


Advantages

  • No need for low-level SSLContext configuration anymore.
  • No knowledge about SSLContext, TrustManager, TrustManagerFactory, KeyManager, KeyManagerFactory, and how to create it is needed.
  • The above classes will all be created by just providing an identity and a trustStore.
  • Create a sslcontext with multiple identities and trustStores.
  • No need for closing streams and null checks.
  • Proven to be working with over 40+ HTTP clients for Java, Kotlin, and Scala
  • Works with servers, for example, Netty, Jetty, or Akka

The library also provides other utilities, such as:

  • CertificateUtils
  • KeyStoreUtils
  • KeyManagerUtils
  • TrustManagerUtils
  • PemUtils
  • SSLContextUtils
  • SSLSessionUtils
  • SSLSocketUtils

See the overview of configurations for all the options.

Returnable Values

The SSLFactory can return different kinds of values for all sorts of use cases. See below for the overview:

Java
 
import nl.altindag.ssl.SSLFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
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();
        Optional<KeyManagerFactory> keyManagerFactory = sslFactory.getKeyManagerFactory();
        Optional<TrustManagerFactory> trustManagerFactory = sslFactory.getTrustManagerFactory();
        List<X509Certificate> trustedCertificates = sslFactory.getTrustedCertificates();
        SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
        SSLServerSocketFactory sslServerSocketFactory = sslFactory.getSslServerSocketFactory();
        SSLEngine sslEngine = sslFactory.getSslEngine(host, port);
        SSLParameters sslParameters = sslFactory.getSslParameters();
        List<String> ciphers = sslFactory.getCiphers();
        List<String> protocols = sslFactory.getProtocols();
    }

}


Tested HTTP Clients

The library has been tested with over 40 different HTTP clients for Java, Kotlin, and Scala. See here for the complete overview, or click one of the links below to jump directly into the source code to view the examples:

Java

  • Apache HttpClient -> Client configuration | Example request
  • Apache HttpAsyncClient -> Client configuration | Example request
  • Apache 5 HttpClient -> Client configuration | Example request
  • Apache 5 HttpAsyncClient -> Client configuration | Example request
  • JDK HttpClient -> Client Configuration | Example request
  • Old JDK HttpClient -> Client Configuration & Example request
  • Netty Reactor -> Client Configuration | Example request
  • Jetty Reactive HttpClient -> Client Configuration | Example request
  • Spring RestTemplate -> Client Configuration | Example request
  • Spring WebFlux WebClient Netty -> Client Configuration | Example request
  • Spring WebFlux WebClient Jetty -> Client Configuration | Example request
  • OkHttp -> Client Configuration | Example request
  • Jersey Client -> Client Configuration | Example request
  • Old Jersey Client -> Client Configuration | Example request
  • Apache CXF JAX-RS -> Client Configuration | Example request
  • Apache CXF using ConduitConfigurer -> Client Configuration | Example request
  • Google HttpClient -> Client Configuration | Example request
  • Unirest -> Client Configuration | Example request
  • Retrofit -> Client Configuration | Example request
  • Async Http Client -> Client Configuration | Example request
  • Feign -> Client Configuration | Example request
  • Methanol -> Client Configuration | Example request
  • Vertx WebClient -> Client Configuration | Example request
  • gRPC -> Client/Server Configuration & Example request
  • ElasticSearch -> RestHighLevelClient Configuration & example request

Kotlin

  • Fuel -> Client Configuration & Example request
  • Http4k with Apache 4 -> Client Configuration | Example request
  • Http4k with Async Apache 4 -> Client Configuration | Example request
  • Http4k with Apache 5 -> Client Configuration | Example request
  • Http4k with Async Apache 5 -> Client Configuration | Example request
  • Http4k with Java Net -> Client Configuration | Example request
  • Http4k with Jetty -> Client Configuration | Example request
  • Http4k with OkHttp -> Client Configuration | Example request
  • Kohttp -> Client Configuration & Example request
  • Ktor with Android engine -> Client Configuration | Example request
  • Ktor with Apache engine -> Client Configuration | Example request
  • Ktor with CIO (Coroutine-based I/O) engine -> Client Configuration | Example request
  • Ktor with Okhttp engine -> Client Configuration | Example request

Scala

  • Twitter Finagle -> Client Configuration | Example request
  • Twitter Finagle Featherbed -> Client Configuration & Example request
  • Akka HTTP Client -> Client Configuration | Example request
  • Dispatch Reboot -> Client Configuration & Example request
  • ScalaJ / Simplified Http Client -> Client Configuration & Example request
  • Sttp -> Client Configuration & Example request
  • Requests-Scala -> Client Configuration & Example request
  • Http4s Blaze Client -> Client Configuration | Example request
  • Http4s Java Net Client -> Client Configuration | Example request

Conclusion

This was a small introduction to get you started with SSL/TLS for an HTTP Client. It provides an alternative setup compared to the traditional one, so you, as a developer, can easily set it up by yourself without the need for low-level knowledge. Looking for a more advanced setup? Please check out all the different configurations here: Overview of configurations.

Requests Java (programming language) unit test

Opinions expressed by DZone contributors are their own.

Related

  • Mastering Unit Testing and Test-Driven Development in Java
  • Comprehensive Guide to Unit Testing Spring AOP Aspects
  • Improving Java Code Security
  • Hints for Unit Testing With AssertJ

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook