How do I use SSL/TLS with Java 11 HttpClient for secure requests?

To use SSL/TLS with Java 11’s HttpClient for secure HTTPS requests, you need to ensure proper configuration of certificates and trust stores. Here’s a detailed step-by-step guide:


1. Default SSL Configuration

By default, the Java 11 HttpClient will handle HTTPS requests securely using the system’s default trust store (java.security settings or cacerts trust store in the JDK).

Here’s how you make a secure request without additional setup:

package org.kodejava.net.http;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class SecureRequestExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://example.com"))
                .GET()
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}

Notes:

  • The default HttpClient uses the default SSLContext for secure connections.
  • The JDK’s default trust store (cacerts) is used to validate the server’s certificate.

2. Customizing the SSL Context

If you need to use a custom trust store or a client certificate, you can set up a custom SSLContext.

Example with a Custom Trust Store:

package org.kodejava.net.http;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.KeyManagerFactory;
import java.io.FileInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;

public class CustomSSLExample {
    public static void main(String[] args) throws Exception {
        // Path to your custom trust store and password
        String trustStorePath = "path/to/truststore.jks";
        String trustStorePassword = "password";

        // Load the trust store
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream trustStream = new FileInputStream(trustStorePath)) {
            trustStore.load(trustStream, trustStorePassword.toCharArray());
        }

        // Initialize TrustManager with the trust store
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);

        // Initialize SSLContext with the trust manager
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

        // Create an HttpClient with the custom SSLContext
        HttpClient client = HttpClient.newBuilder()
                .sslContext(sslContext)
                .build();

        // Make a secure HTTPS request
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://example.com"))
                .GET()
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}

Explanation:

  • A custom trust store is loaded and used to validate the server’s certificate against your specific CA.
  • No client-side certificates are used here.

3. Using Client Certificates

To configure a client certificate, you’ll need a KeyManager in addition to the TrustManager.

Example with Client Certificates:

package org.kodejava.net.http;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;

public class ClientCertificateExample {
    public static void main(String[] args) throws Exception {
        // Path to your client key store, trust store, and their passwords
        String keyStorePath = "path/to/keystore.jks";
        String keyStorePassword = "keystorePassword";
        String trustStorePath = "path/to/truststore.jks";
        String trustStorePassword = "truststorePassword";

        // Load KeyStore (for client certificate)
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream keyStream = new FileInputStream(keyStorePath)) {
            keyStore.load(keyStream, keyStorePassword.toCharArray());
        }

        // Load TrustStore (for server certificate)
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream trustStream = new FileInputStream(trustStorePath)) {
            trustStore.load(trustStream, trustStorePassword.toCharArray());
        }

        // Initialize KeyManager
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());

        // Initialize TrustManager
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);

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

        // Create HttpClient
        HttpClient client = HttpClient.newBuilder()
                .sslContext(sslContext)
                .build();

        // Send a secure request
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://example.com"))
                .GET()
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}

4. Configure the JVM to Use a Custom Trust Store

An alternative to programmatically setting up the SSLContext is to configure the JVM to use a custom trust store at runtime by defining system properties:

-Djavax.net.ssl.keyStore=path/to/keystore.jks
-Djavax.net.ssl.keyStorePassword=keystorePassword
-Djavax.net.ssl.trustStore=path/to/truststore.jks
-Djavax.net.ssl.trustStorePassword=truststorePassword

This will make the custom key store and trust store available globally without modifying any Java code.


Debugging SSL Issues

If you face SSL/TLS-related issues, enable debugging by setting the following JVM option:

-Djavax.net.debug=ssl

This will print detailed information about the SSL handshake.


Summary

  • Use the default HttpClient for standard HTTPS requests.
  • Configure a custom SSLContext with TrustManager and/or KeyManager for advanced configurations like custom trust stores or client certificates.
  • Alternatively, use JVM system properties to configure trust/key stores globally.

How do I send email using Gmail via SSL?

In another example you’ve seen how to send email using Gmail SMTP via TLS. See the following example How do I send email using Gmail via TLS?.

In this example you will use the SSL connection to connect to Gmail SMTP server. The difference with this is in the properties / configuration that we used to create the mail session object.

package org.kodejava.mail;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.Properties;

public class GmailSendEmailSSL {
    private static final String USERNAME = "[email protected]";
    private static final String PASSWORD = "password";

    public static void main(String[] args) throws Exception {
        // Email information such as from, to, subject and contents.
        String mailFrom = "[email protected]";
        String mailTo = "[email protected]";
        String mailSubject = "SSL - Gmail Send Email Demo";
        String mailText = "SSL - Gmail Send Email Demo";

        GmailSendEmailSSL gmail = new GmailSendEmailSSL();
        gmail.sendMail(mailFrom, mailTo, mailSubject, mailText);
    }

    private void sendMail(String mailFrom, String mailTo, String mailSubject,
                          String mailText) throws Exception {

        Properties config = createConfiguration();

        // Creates a mail session. We need to supply username and
        // password for Gmail authentication.
        Session session = Session.getInstance(config, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(
                        GmailSendEmailSSL.USERNAME,
                        GmailSendEmailSSL.PASSWORD
                );
            }
        });

        // Creates email message
        Message message = new MimeMessage(session);
        message.setSentDate(new Date());
        message.setFrom(new InternetAddress(mailFrom));
        message.setRecipient(Message.RecipientType.TO, new InternetAddress(mailTo));
        message.setSubject(mailSubject);
        message.setText(mailText);

        // Send a message
        Transport.send(message);
    }

    private Properties createConfiguration() {
        return new Properties() {{
            put("mail.smtp.host", "smtp.gmail.com");
            put("mail.smtp.auth", "true");
            put("mail.smtp.port", "465");
            put("mail.smtp.socketFactory.port", "465");
            put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            put("mail.smtp.ssl.protocols", "TLSv1.2");
        }};
    }
}

There is a chance that you can get an error that tell you cannot connect to the smtp.gmail.com port 465. If this is the case please check your connection to the server using telnet command. The command is telnet smtp.gmail.com 465, and see if there is a reply.

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>javax.mail-api</artifactId>
        <version>1.5.6</version>
    </dependency>
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>mail</artifactId>
        <version>1.4.7</version>
    </dependency>
</dependencies>

Maven Central Maven Central