Enterprise Java

Securing Spring Boot 3 applications using SSL Bundles

Securing data in transit is a crucial aspect of web application security, and one effective way to achieve this is through the use of SSL/TLS certificates. Securing your Spring Boot applications is crucial to protect sensitive data and ensure the integrity of communication. This article serves as a guide, shedding light on the role of Spring Boot Security SSL bundles in fortifying applications against potential threats, and also demonstrates how to configure RestTemplate to communicate securely.

1. Understanding SSL/TLS Certificates

SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) are cryptographic protocols that provide secure communication over a computer network. SSL/TLS certificates play a vital role in establishing a secure connection between a client and a server by encrypting the data transmitted between them. SSL/TLS ensures the confidentiality and integrity of data in transit.

1.1 Types of SSL Certificates

  • Self-Signed Certificates: These certificates are generated by the server itself. While they provide encryption, they lack the trust factor as they are not signed by a trusted Certificate Authority (CA). They are suitable for testing or internal use.
  • CA-Signed Certificates: Issued by a trusted Certificate Authority, these certificates are ideal for production environments. They establish trust between the client and the server, ensuring a secure connection.

1.2 What are SSL Bundles?

SSL Bundles introduced in Spring Boot 3.1 simplify SSL/TLS configuration by unifying keystores, certificates, and private keys into a single entity. 

An SSL Bundle encapsulates all trust-related components and configuration parameters, including keystores, certificates, and private keys, into a single and easily controllable entity. After configuring an SSL Bundle, it can be employed for one or multiple network connections, irrespective of whether they are incoming or outgoing.

1.2.1 Why Use SSL Bundles for Security?

  • Encryption: Encrypts data communication between your application and external services, preventing eavesdropping.
  • Authentication: Verifies the identity of servers and clients, minimizing the risk of impersonation.
  • Data Integrity: Ensures transmitted data remains unaltered, protecting against tampering.
  • Simplified Configuration: SSL Bundles offer a cleaner and more manageable approach compared to scattered server.ssl.* properties.

2. Securing RestTemplate with SSL Bundles

Here’s how to use SSL Bundles with RestTemplate for secure REST API calls:

2.1 Generate SSL Certificates

The first step involves generating SSL certificates. In this article, we’ll use a self-signed certificate. In a production environment, obtaining a CA-signed certificate is recommended. Open a terminal or command prompt and run the following command from the project root of the spring boot application:

# Generate a self-signed certificate
keytool -genkeypair -alias myapplication -keyalg RSA -keysize 4096 -storetype PKCS12 -keystore keystore.p12 -validity 3650

The above command uses Java keytool utility, which is a key and certificate management tool included with the Java Development Kit (JDK) to generate a key pair and store it in a keystore file in PKCS12 format. Note that you’ll be prompted to enter some additional information like your first and last name, organizational unit, city or locality, and your two-letter country code details.

Here’s a breakdown of the command and its parameters:

  • -alias myapplication: This sets the alias for the key pair to myapplication. An alias is a name that is used to reference the key pair within the keystore.
  • -keysize 4096: This sets the key size to 4096 bits. Larger key sizes generally provide stronger security, but they may also be computationally more expensive.
  • -storetype PKCS12: This specifies the type of keystore to be created. PKCS12 is a standard format for storing private keys with their corresponding public key certificates.
  • -keystore keystore.p12: This sets the filename of the keystore to be created to keystore.p12.
  • -validity 3650: This sets the validity period of the key pair in days. In this case, it’s set to 3650 days, which is equivalent to 10 years.

2.2 Configure Spring Boot for SSL

With Spring boot 3.1, we can set up SSL Bundle properties and apply various certificates to connect to one or more connections, like RestTemplate or an embedded server. In your Spring Boot application, configure the SSL properties in the application.properties or application.yml file:

application.yml

spring:
  ssl:
    bundle:
      jks:
        server:
          key:
            alias: "myapplication"
          keystore:
            location: "classpath:keystore.p12"
            password: "javacodegeeks"
            type: "PKCS12"

application.properties

server.port=8443
server.ssl.bundle=server
spring.ssl.bundle.jks.server.key.alias=myapplication
spring.ssl.bundle.jks.server.keystore.location=classpath:keystore.p12
spring.ssl.bundle.jks.server.keystore.password=javacodegeeks
spring.ssl.bundle.jks.server.keystore.type=PKCS12

The above configuration will configure a JKS-based certificate for the Spring boot embedded server.

Let’s break down the configuration in the provided application.properties file:

  • server.port=8443:
    • This configuration sets the port on which the embedded server (typically Tomcat) will listen for incoming HTTPS requests. In this case, it is set to 8443, which is a common default port for secure connections.
  • server.ssl.bundle=server:
    • This property is a configuration indicating the name of the SSL bundle. In this case, the name is set to server.
  • spring.ssl.bundle.jks.server.key.alias=myapplication:
    • Specifies the alias of the key entry in the keystone. In this case, the alias is set to myapplication.
  • spring.ssl.bundle.jks.server.keystore.location=classpath:keystore.p12:
    • Specifies the location of the keystore file. In this example, the Keystore is a PKCS12 file located on the classpath and named keystore.p12. The classpath: prefix indicates that the keystore is bundled with the application.
  • spring.ssl.bundle.jks.server.keystore.password=javacodegeeks:
    • Sets the password for accessing the keystore. Ensure that this password matches the actual password used to protect the keystore file.
  • spring.ssl.bundle.jks.server.keystore.type=PKCS12:
    • Specifies the type of keystore. In this case, it’s set to PKCS12, indicating that the keystore follows the PKCS12 format. PKCS12 is a commonly used format for keystores, especially in Java applications.

To configure another PEM certificate for a RestClient, we will use the same keystore.p12 certificate and convert it to a PEM-based .crt and .key file and use it as another configuration for PEM.

openssl pkcs12 -in keystore.p12 -nokeys -out src/main/resources/client.crt 
openssl pkcs12 -in keystore.p12 -nocerts -nodes -out src/main/resources/private.key

Update the application.properties file to use spring.ssl.bundle.pem to configure bundles for the generated PEM-encoded files like this:

spring.ssl.bundle.pem.client.keystore.certificate=classpath:client.crt
spring.ssl.bundle.pem.client.keystore.private-key=classpath:private.key

2.3 Create a Service

Create a service named ProductService to use the configured RestTemplate bean to call a free remote API on (https://jsonplaceholder.typicode.com/).

@Service
public class ProductService {

    private final RestTemplate restTemplate;

    @Autowired
    public ProductService(RestTemplate restTemplate) {

        this.restTemplate = restTemplate;

    }

    public String remoteRestCall() {

        return this.restTemplate.getForObject("https://jsonplaceholder.typicode.com/todos", String.class);
    }

}

The above code utilizes a RestTemplate to make a remote REST call by sending an HTTP GET request to the specified URL (“https://jsonplaceholder.typicode.com/todos“).

2.4 Create a Controller

Create a controller named ProductController. For simplicity, our controller returns a JSON string and also contains a method that is used to delegate the handling of the HTTP request to our service class (ProductService). This method invokes the remoteRestCall() method from the productService bean.

@RestController
@RequestMapping("/api")
public class ProductController {

    private final ProductService productService;

    @Autowired
    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping("/product")
    public String product() {
        // Construct a JSON string (for simplicity, using a hardcoded string)
        String jsonString = "{\"name\": \"Real World Java EE Patterns\"}";
        return jsonString;
    }

    @GetMapping("/testssl")
    public String remoteRestCall() {
        return productService.remoteRestCall();
    }
     
}

Next, let’s configure a RestTemplate bean using a RestTemplateBuilder and an SslBundles bean.

@SpringBootApplication
public class SecureappWithSslApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecureappWithSslApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
        return restTemplateBuilder.setSslBundle(sslBundles.getBundle("server")).build();
    }

}

The above code configures a RestTemplate instance using the provided RestTemplateBuilder. The setSslBundle() method is called on restTemplateBuilder to apply an SSL bundle, and sslBundles.getBundle("server") retrieves the SSL bundle named server from the SslBundles bean.

3. Run and Test the Application

Now, when we can run our Spring Boot application, we should see the following log messages on the terminal console:

main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https)

Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/Users/username/.keystore] using alias [myapplication] with trust store [null]

main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8443 (https) with context path ''


These log messages confirm that the application is started and running on port 8443 using HTTPS. Now we can make our request with curl or by opening a browser and navigating to our endpoint URI at https://localhost:8443/api/product.

The output of the application in a web browser should look like this:

Fig 1: output of spring boot application configured with security ssl bundles
Fig 1: output of spring boot application configured with security ssl bundles

We can use the curl command to make a request to the free remote API and get some information about the SSL handshake that happened with the following command:

curl --verbose --insecure https://localhost:843/api/testssl

We should get the following output:

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=NG; ST=lagos; L=lagos; O=jcg; OU=jcg; CN=omos aziegbe
*  start date: Jan 24 13:03:58 2024 GMT
*  expire date: Jan 21 13:03:58 2034 GMT
*  issuer: C=NG; ST=lagos; L=lagos; O=jcg; OU=jcg; CN=omos aziegbe
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
> GET /api/testssl HTTP/1.1
> Host: localhost:8443
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 24311
< Date: Thu, 25 Jan 2024 17:09:04 GMT
< 
[
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "quis ut nam facilis et officia qui",
    "completed": false
  },

From the output, we can see our server certificate with the following information subject: C=NG; ST=lagos; L=lagos; O=jcg; OU=jcg; CN=omos aziegbe about our SSL certificate.

4. Conclusion

In this article, we walked through the process of obtaining SSL certificates, configuring a Spring Boot application for SSL, setting up RestTemplate to communicate securely, and creating a simple controller to test secure communication.

Furthermore, the article presented a full example code snippet demonstrating using application.properties for configuring SSL bundles. This included setting the server port, SSL bundle information, and keystore details.

In conclusion, we can enhance the security of our Spring Boot 3 applications to protect sensitive information and establish a foundation for secure communication over HTTPS. A final note is that It is crucial to use trusted SSL certificates in production environments to ensure a more secure and trusted application.

5. Download the Source Code

This was an example of securing Spring Boot 3 applications using security SSL bundles.

Download
You can download the full source code of this example here: spring boot security ssl bundles

Omozegie Aziegbe

Omos holds a Master degree in Information Engineering with Network Management from the Robert Gordon University, Aberdeen. Omos is currently a freelance web/application developer who is currently focused on developing Java enterprise applications with the Jakarta EE framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Vijay
Vijay
3 months ago

i dont understand the purpose of pem file created here. i see they are used any where, can you explain?

Back to top button