Introduction
In the realm of secure communication over networks, Mutual Transport Layer Security (mTLS) stands out as a robust protocol for ensuring both authentication and encryption between client and server. Building a Java client-server application with mTLS not only enhances security but also establishes a trustworthy connection where both parties can verify each other’s identities.
In this tutorial, we will embark on a journey to create a Java client-server application leveraging the power of mTLS. We’ll delve into the intricacies of setting up a secure communication channel, where both the client and server authenticate each other using digital certificates, and data exchanged between them remains encrypted throughout the transmission.
Through step-by-step instructions, we’ll explore how to generate and manage digital certificates, configure the Java server and client to enable mTLS, and establish a secure channel for communication. By the end of this tutorial, you’ll have a deep understanding of how to implement mTLS in Java applications, equipping you with the knowledge to build secure and reliable client-server architectures.
Let’s embark on this journey to harness the power of mTLS and fortify the communication channels of our Java applications.
Step 1: Generate Certificate Authority (CA)
- Create a CA Key and Certificate:
First, generate a private key for the Certificate Authority (CA) and then create a self-signed certificate for the CA.
openssl genrsa -out ca-key.pem 2048
openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 365
Step 2: Create Server Certificate
- Generate Server Key and CSR:
Next, create a private key and a Certificate Signing Request (CSR) for the server.
openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server-csr.pem
- Sign the Server Certificate with CA:
Use the CA’s private key to sign the server’s CSR, creating the server certificate.
openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -out server-cert.pem -days 365 -CAcreateserial
Step 3: Create Client Certificate
- Generate Client Key and CSR:
Similarly, create a private key and CSR for the client.
openssl genrsa -out client-key.pem 2048
openssl req -new -key client-key.pem -out client-csr.pem
- Sign the Client Certificate with CA:
Sign the client’s CSR with the CA to create the client certificate.
openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -out client-cert.pem -days 365 -CAcreateserial
Now that we have the necessary certificates, let’s create the Java programs for the server and client.
Step 4: Java Server Program
- Java Server Code:
Create a Java server that loads the server certificate and private key, and sets up an HTTPS server with mutual TLS.
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
public class MTLSHttpServer {
public static void main(String[] args) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
String keystorePath = "server-keystore.jks";
char[] keystorePass = "password".toCharArray();
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(keystorePath), keystorePass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keyStore, keystorePass);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
HttpsServer server = HttpsServer.create(new InetSocketAddress(8443), 0);
server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
SSLContext context = SSLContext.getDefault();
SSLEngine engine = context.createSSLEngine();
params.setNeedClientAuth(true);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
SSLParameters sslParameters = context.getDefaultSSLParameters();
params.setSSLParameters(sslParameters);
} catch (Exception ex) {
System.out.println("Failed to create HTTPS port");
}
}
});
server.createContext("/", (exchange -> {
String response = "Hello, this is a secure server!";
exchange.sendResponseHeaders(200, response.length());
exchange.getResponseBody().write(response.getBytes());
exchange.getResponseBody().close();
}));
server.start();
System.out.println("Server running on port 8443");
}
}
Step 5: Java Client Program
- Java Client Code:
Now, create a Java client that loads the client certificate and key, and performs an HTTPS request to the server with mutual TLS.
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
public class MTLSHttpClient {
public static void main(String[] args) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
String keystorePath = "client-keystore.jks";
char[] keystorePass = "password".toCharArray();
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(keystorePath), keystorePass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keyStore, keystorePass);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL url = new URL("https://localhost:8443/");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
int responseCode = conn.getResponseCode();
System.out.println("Response Code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println("Response: " + response.toString());
} else {
System.out.println("Request failed");
}
}
}
Step 6: Run the Server and Client
- Run the Server:
Compile and run theMTLSHttpServer
program. Make sureserver-cert.pem
,server-key.pem
, andca-cert.pem
are in the same directory, or update the paths in the server code.
javac MTLSHttpServer.java
java MTLSHttpServer
- Run the Client:
Compile and run theMTLSHttpClient
program. Ensureclient-cert.pem
,client-key.pem
, andca-cert.pem
are in the same directory, or update the paths in the client code.
javac MTLSHttpClient.java
java MTLSHttpClient
Notes:
- Make sure to adjust paths and filenames as per your actual file locations and names.
- You’ll need the Java KeyStore (JKS) files for both server and client, which can be created from the PEM files using
keytool
. - This example uses Java’s built-in `HttpsServer
This setup will allow the Java client and server to communicate over HTTPS with mutual TLS authentication using the generated certificates.