Spring Boot One-Way TLS: Secure HTTPS Calls with RestTemplate Explained

2 min read

Spring Boot One-Way TLS: Secure HTTPS Calls with RestTemplate Explained

1. Introduction

Transport Layer Security (TLS) is essential for protecting data exchanged between services. In many enterprise systems, microservices need to communicate securely using HTTPS while validating server certificates. One-Way TLS (also known as Server Authentication) ensures that a Spring Boot client verifies the identity of the server it connects to. This article demonstrates how to configure One-Way TLS using RestTemplate in a Spring Boot application, including keystore setup, SSL context configuration, and code examples.

2. Problem

When a Spring Boot application communicates with another HTTPS service, it typically relies on default JDK trust settings. This becomes an issue when:

  • The remote server uses a private certificate authority (CA)
  • The certificate is self-signed
  • Additional trust anchors are required

Without the correct trust configuration, Spring Boot clients fail with errors such as:

javax.net.ssl.SSLHandshakeException: PKIX path building failed

3. Solution

One-Way TLS requires only the client to validate the server certificate. The client does not present its own certificate.

The solution consists of:

  • Creating a truststore containing the server certificate or CA certificate
  • Configuring Spring Boot to load the truststore
  • Building an SSLContext with the truststore
  • Registering a custom RestTemplate
  • Making secure HTTPS calls with server authentication

4. Implementation

4.1 Extract the Server Certificate

openssl s_client -connect secure-api.example.com:443 -showcerts /dev/null | openssl x509 -outform PEM > server.crt

4.2 Create a Truststore

keytool -importcert \
  -alias secure-api \
  -file server.crt \
  -keystore truststore.p12 \
  -storetype PKCS12 \
  -storepass changeit

Place truststore.p12 inside src/main/resources/ssl.

4.3 application.properties

server.ssl.enabled=false

app.ssl.trust-store=classpath:ssl/truststore.p12
app.ssl.trust-store-password=changeit
app.ssl.trust-store-type=PKCS12

4.4 SSL Configuration Class

import org.apache.hc.client5.http.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.security.KeyStore;

@Configuration
public class OneWayTLSConfig {

    @Value("${app.ssl.trust-store}")
    private String trustStorePath;

    @Value("${app.ssl.trust-store-password}")
    private String trustStorePassword;

    @Value("${app.ssl.trust-store-type}")
    private String trustStoreType;

    @Bean
    public RestTemplate restTemplate() throws Exception {
        SSLContext sslContext = createSSLContext();

        SSLConnectionSocketFactory socketFactory =
                new SSLConnectionSocketFactory(sslContext);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(socketFactory)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);

        return new RestTemplate(requestFactory);
    }

    private SSLContext createSSLContext() throws Exception {
        KeyStore trustStore = KeyStore.getInstance(trustStoreType);

        try (InputStream trustStream =
                     getClass().getResourceAsStream(trustStorePath.replace("classpath:", ""))) {
            trustStore.load(trustStream, trustStorePassword.toCharArray());
        }

        return SSLContexts.custom()
                .loadTrustMaterial(trustStore, null)
                .build();
    }
}

4.5 Example Service Using RestTemplate

import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class SecureApiService {

    private final RestTemplate restTemplate;

    public SecureApiService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String getSecureData() {
        String url = "https://secure-api.example.com/data";
        return restTemplate.getForObject(url, String.class);
    }
}

5. Conclusion

One-Way TLS is an essential security layer for Spring Boot applications that consume HTTPS services. By configuring a custom truststore and wiring a TLS-enabled RestTemplate, applications can safely authenticate servers using private or self-signed certificates. This approach provides secure, reliable communication across microservices and external APIs without requiring mutual TLS (mTLS).

🤞 Never miss a story from us, get weekly updates to your inbox!

Leave a Reply

Your email address will not be published. Required fields are marked *