1. Introduction
Managing secure communications in Spring Boot applications often involves dealing with complex configurations. The challenges usually start with handling trust material, such as certificates and private keys, that come in various formats like JKS, PKCS #12, or PEM. Each of these formats has its own set of requirements for how it should be processed.
Fortunately, Spring Boot 3.1 introduces SSL Bundles, a feature designed to simplify these complexities. In this tutorial, we’ll explore what SSL Bundles are and how they can streamline SSL configuration tasks for Spring Boot applications.
2. Spring Boot SSL Bundles
Usually, once we’ve obtained the trust material, we need to turn it into Java objects that the application can work with. This often means dealing with classes like java.security.KeyStore for storing key material, javax.net.ssl.KeyManager for managing this key material, and javax.net.ssl.SSLContext for creating secure socket connections.
Each of these classes requires another layer of understanding and configuration, making the process tedious and error-prone. Various Spring Boot components might also necessitate delving into different layers of abstraction to apply these settings, adding another level of difficulty to the task.
An SSL Bundle encapsulates all the trust material and configuration settings, such as keystores, certificates, and private keys, into a single, easily manageable unit. Once an SSL Bundle is configured, it can be applied to one or more network connections, whether they are incoming or outgoing.
Configuration properties for SSL Bundles reside under the spring.ssl.bundle prefix in the application.yaml or application.properties configuration files.
Let’s start with JKS Bundles. We use spring.ssl.bundle.jks to configure bundles that use Java Keystore files:
spring:
ssl:
bundle:
jks:
server:
key:
alias: "server"
keystore:
location: "classpath:server.p12"
password: "secret"
type: "PKCS12"
In the case of PEM Bundles, we use spring.ssl.bundle.pem to configure bundles using PEM-encoded text files:
spring:
ssl:
bundle:
pem:
client:
truststore:
certificate: "classpath:client.crt"
Once these bundles are configured, they can be applied across microservices—whether it’s an Inventory Service needing to securely access a database, a User Authentication Service requiring secure API calls, or a Payment Processing Service securely communicating with a payment gateway.
Spring Boot automates the creation of Java objects like KeyStore, KeyManager, and SSLContext based on SSL Bundle configurations. This removes the need to manually create and manage these objects, making the process more straightforward and less prone to errors.
3. Securing RestTemplate With SSL Bundles
Let’s start with leveraging SSL Bundles while using the RestTemplate bean. For that, we’ll use a sample Spring Boot app, but first, we need to generate keys that will be used as an SSL Bundle.
We’ll use the openssl binary (that’s usually installed along with git) to generate keys by executing the following command from the project root:
$ openssl req -x509 -newkey rsa:4096 -keyout src/main/resources/key.pem -out src/main/resources/cert.pem -days 365 -passout pass:FooBar
Now, let’s convert this key to the PKCS12 format:
$ openssl pkcs12 -export -in src/main/resources/cert.pem -inkey src/main/resources/key.pem -out src/main/resources/keystore.p12 -name secure-service -passin pass:FooBar -passout pass:FooBar
As a result, we have everything for configuring SSL bundles; let’s define a bundle named “secure-service” in the application.yml file:
spring:
ssl:
bundle:
jks:
secure-service:
key:
alias: "secure-service"
keystore:
location: "classpath:keystore.p12"
password: "FooBar"
type: "PKCS12"
Next, we can set our bundle on RestTemplate by calling the setSslBundle() method:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
return restTemplateBuilder.setSslBundle(sslBundles.getBundle("secure-service")).build();
}
Finally, we can use the configured RestTemplate bean to call an API:
@Service
public class SecureServiceRestApi {
private final RestTemplate restTemplate;
@Autowired
public SecureServiceRestApi(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String fetchData(String dataId) {
ResponseEntity<String> response = restTemplate.exchange(
"https://secure-service.com/api/data/{id}",
HttpMethod.GET,
null,
String.class,
dataId
);
return response.getBody();
}
}
The SSL Bundle in our Spring Boot application serves to validate the certificate of the secure-service, ensuring an encrypted and secure communication channel. However, this doesn’t limit us from using client certificates for authentication on the API side. We’ll see later how to obtain SSLContext to configure custom clients.
4. Leveraging Spring Boot’s Auto-Configured SSLBundles
Before Spring Boot’s SSL Bundles, developers used to work with the classic Java classes that underpin SSL configurations:
- java.security.KeyStore: These instances are used as keystores and truststores, effectively serving as secure repositories of cryptographic keys and certificates.
- javax.net.ssl.KeyManager and javax.net.ssl.TrustManager: These instances manage the keys and trust decisions during SSL communications, respectively.
- javax.net.ssl.SSLContext: These instances act as a factory for SSLEngine and SSLSocket objects, orchestrating how SSL configurations are implemented at runtime.
Spring Boot 3.1 introduces a structured abstraction layer divided into Java interfaces:
- SslStoreBundle: Offers a gateway to KeyStore objects containing cryptographic keys and trusted certificates.
- SslManagerBundle: Coordinates and provides methods to manage KeyManager and TrustManager objects.
- SslBundle: Serves as a one-stop shop, aggregating all these functionalities into a unified interaction model with the SSL ecosystem.
Subsequently, Spring Boot auto-configures an SslBundles bean. As a result, we can conveniently inject SslBundle instances into any Spring Bean. This is exceptionally useful for configuring secure communications for legacy codebase and custom REST clients.
For example, let’s consider a custom SSLContext is needed for a custom secure HttpClient:
@Component
public class SecureRestTemplateConfig {
private final SSLContext sslContext;
@Autowired
public SecureRestTemplateConfig(SslBundles sslBundles) throws NoSuchSslBundleException {
SslBundle sslBundle = sslBundles.getBundle("secure-service");
this.sslContext = sslBundle.createSslContext();
}
@Bean
public RestTemplate secureRestTemplate() {
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create().setSslContext(this.sslContext).build();
HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslSocketFactory).build();
HttpClient httpClient = HttpClients.custom().setConnectionManager(cm).evictExpiredConnections().build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
}
In the code above, we inject the SslBundles instance into the Autowired constructor. Actually, SslBundles provides us access to all configured SSL Bundles. Therefore, we retrieve the secure-service bundle and create the context. Later, we use the SSLContext instance to create a custom HttpClient and apply it to create a RestTemplate bean.
5. Using SSL Bundles With Data Services
Different data services have varying degrees of SSL configuration options, creating complexities during the configuration process.
SSL Bundles introduces a more uniform approach to SSL configurations across a wide range of data services:
- Cassandra: spring.cassandra.ssl
- Couchbase: spring.couchbase.env.ssl
- Elasticsearch: spring.elasticsearch.restclient.ssl
- MongoDB: spring.data.mongodb.ssl
- Redis: spring.data.redis.ssl
Now, most of these services support a *.ssl.enabled property. This property activates SSL support in the client library, leveraging the trust material found in the Java runtime’s cacerts.
Additionally, the *.ssl.bundle property allows us to apply a named SSL bundle to customize the trust material, thereby achieving uniformity and reusability across multiple service connections.
For this example, let’s assume that there’s an SSL bundle named mongodb-ssl-bundle. This bundle contains the necessary trust material to secure connections to a MongoDB instance.
Let’s define the application.yml file:
spring:
data:
mongodb:
ssl:
enabled: true
bundle: mongodb-ssl-bundle
By simply adding these properties, the MongoDB client library in the Spring Boot application automatically uses the SSL context and trust material specified in mongodb-ssl-bundle.
6. Using SSL Bundles With Embedded Servers
Managing SSL configurations for embedded web servers in Spring Boot can be also simplified by using SSL Bundles.
Traditionally, the server.ssl.* properties have been used for setting each individual SSL configuration. With SSL Bundles, configurations can be grouped together and then reused across multiple connections, reducing the chance of mistakes and simplifying overall management.
Firstly, let’s explore the traditional, individual property approach:
server:
ssl:
key-alias: "server"
key-password: "keysecret"
key-store: "classpath:server.p12"
key-store-password: "storesecret"
client-auth: NEED
On the other hand, the SSL Bundle approach allows for the same configurations to be encapsulated:
spring:
ssl:
bundle:
jks:
web-server:
key:
alias: "server"
password: "keysecret"
keystore:
location: "classpath:server.p12"
password: "storesecret"
Now, we can use the defined bundle for securing our web server:
server:
ssl:
bundle: "web-server"
client-auth: NEED
While both approaches secure an embedded web server, the SSL Bundle method is more efficient for configuration management. This feature is also available for other configurations like management.server.ssl and spring.rsocket.server.ssl.
7. Conclusion
In this article, we explored the new SSL Bundles feature in Spring Boot that can simplify and unify the process of configuring trust materials.
In contrast to traditional server.ssl.* properties, SSL Bundles offer a structured way to manage both keystores and truststores. This is particularly beneficial for reducing misconfiguration risks and increasing the efficiency of managing SSL across multiple services.
Furthermore, SSL Bundles lend themselves well to centralized management, allowing the same bundle to be reused across different parts of the application.
By incorporating SSL bundles, developers can not only simplify the configuration process but also elevate the security posture of embedded web servers in Spring Boot applications.
As always, the entire code examples can be found over on GitHub.