• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Contact Us

All about code

  • Java
    • Spring
      • Spring Boot
      • Spring Cloud
    • Java Miscellaneous Tips
    • Java Design Patterns
  • Data Structures and Algorithms
    • Algorithms
    • Data Structures
  • Thoughts and Stories
  • AI

How to Secure Spring Cloud Applications with SSL Protocol

apple smartphone desk laptop 1282241

Introduction

Securing a microservice system involves several aspects. One concern that we must deal with is the data exposed by services. We want to be sure that the data is encrypted while it flows from the server to the client. Also, we must be sure that our system is made of mutually trusted services, enforced to authenticate with each other before starting the communication. SSL is a protocol that deals with these issues. This article will show how to secure Spring Cloud applications with the SSL protocol.

Securing Application Data Exposed by a Service with SSL Protocol

A common scenario is to serve the REST API of a Spring Boot application over HTTPS protocol to a generic client. HTTPS can be interpreted as HTTP plus SSL protocol. Usually, this is implemented by the gateway component of a micro-service system since all the external requests by generic clients (usually browsers) have to go through it. We want to ensure that all information returned by the server is available as plain data only to the client part, and not understandable by any program that could intercept the communication somehow.

We need a certificate to configure the server to expose its API over HTTPS. If we work in a development environment, we can generate a self-signed certificate. We can use the Java keytool application in the command prompt for this:

keytool -genkey -alias books -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore books.p12 -validity 3650 -dname "CN=codingstrain,OU=cs,O=cs,C=IT" -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -storepass mypass -keypass mypass

The above command generates a pair of asymmetric keys and stores them in a keystore named books.p12. We can copy this file in the application resources folder to make it available on the classpath. Then we have to edit the application.yaml file and add the following configuration:

server:
  port: ${PORT:8092}
  ssl:
     enabled: true
     key-store: classpath:books.p12
     key-store-password: mypass
     key-store-type: PKCS12
    
security:
  require-ssl: true

With the above configuration, the server API will be exposed through HTTPS/SSL, and the data will be encrypted by the private key available in the key-store file.

Securing Services and Discovery Server with SSL protocol

Forcing a Spring Boot application to expose its API through HTTPS and the SSL protocol is a first step toward security, but it’s not enough. A micro-service system is made of services interacting with each other and usually interacting with the external world through a gateway component. We don’t want external clients to be able to directly call services behind the gateway.

We expect our services to accept calls only from other internal services, plus the gateway. In other words, we must have a set of mutually trusted services. To do this, we must configure them to implement a bi-directional SSL connection, in which each node must authenticate the other node before initiating the communication. With this in place, and the exchange data being encrypted, we have a more solid security.

Since our services normally register themselves with a discovery server and fetch other services from it, we also have to secure this mutual interaction. We will therefore describe here the configuration of a specific server and an Eureka discovery server as an example that can be generalized for any couple of services.

We can still use the keytool Java application to generate self-signed certificates. Besides storing the key-store file generated for the service in its resources folder, along with the private key, we also have to provide the public key to the Eureka server. Vice versa, we must provide the public key of the Eureka server to the service.

We provide the public key by importing it into a trust store, which will then be stored in the application that needs to authenticate the other, in the resources folder. Supposing we have a discovery application and a service named books, we can proceed to create the self-signed certificates for the two:

keytool -genkey -alias discovery -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore discovery.p12 -validity 3650 -dname "CN=codingstrain,OU=cs,O=cs,C=IT" -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -storepass mypass -keypass mypass
keytool -genkey -alias books -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore books.p12 -validity 3650 -dname "CN=codingstrain,OU=cs,O=cs,C=IT" -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -storepass mypass -keypass mypass

We obtain two key stores that contain pairs of private and public keys. We then extract the certificates containing the public keys with the following commands:

keytool -exportcert -alias discovery -keystore discovery.p12 -file discovery.cer
keytool -exportcert -alias books -keystore books.p12 -file books.cer

Finally, we import the discovery certificate into the books key-store and the books certificate into the discovery key-store. In this example, we used the same file for two purposes: holding the private key for the specific node and the public key certificate of the service it needs to interact with. We could also use separate store files; it’s just a matter of convenience. From the standpoint of storing the public key certificate, the key-store file can be called a trust-store. We can see here how we can perform the import:

keytool -importcert -alias books -keystore discovery.p12 -file books.cer
keytool -importcert -alias discovery -keystore books.p12 -file discovery.cer

Configuring the Discovery Server to use SSL Protocol

After having put the key-store/trust-store file in the resources folder of the discovery server, we can configure it by editing the application.yaml file:

server:  
  port: ${PORT:8760}
  ssl:
    enabled: true
    client-auth: need
    key-store: classpath:discovery.p12
    key-store-type: PKCS12
    key-store-password: mypass
    trust-store: classpath:discovery.p12
    trust-store-type: PKCS12
    trust-store-password: mypass
    key-alias: discovery
    
security:
  require-ssl: true
        
eureka:
  instance:
    hostname: localhost
    securePort: ${server.port}
    securePortEnabled: true  
    nonSecurePortEnabled: false 
    secureVirtualHostName: ${spring.application.name}
    homePageUrl: https://${eureka.instance.hostname}:${server.port}/
    statusPageUrl: https://${eureka.instance.hostname}:${server.port}/admin/info
    metadataMap:
      hostname : ${eureka.instance.hostname}
      securePort: ${server.port}
  client:
    registerWithEureka: false
    fetchRegistry: false

Firstly, we set the security:require-ssl property to true, to force the application to serve external calls only through SSL, and thus by HTTPS protocol. Then the server:SSL property will contain the configuration for the key-store and trust-store: the type, which is PKCS12 in this case, the path of the file on the classpath, and the password.

We configure the eureka:instance section with securePortEnabled to true and nonSecurePortEnabled to false, forcing a secure communication mode with the services. Finally, in the eureka:client section we simply state that the discovery server should not register itself and should not fetch services from itself.

Configuring the Micro-Service to use SSL Protocol

The configuration for the micro-services is similar, except eureka:client part that does not contain the registerWithEureka and fetchRegistry properties, which are by default true. The eureka:client section contains the URL of the discovery server and the paths and passwords of the key-store and trust-store files.

server:
  port: ${PORT:8092}
  ssl:
     enabled: true
     key-store: classpath:books.p12
     key-store-password: mypass
     key-store-type: PKCS12
     trust-store: classpath:books.p12
     trust-store-password: mypass
    
security:
  require-ssl: true
  
eureka:
  instance:
    hostname: localhost
    securePort: ${server.port}
    securePortEnabled: true  
    nonSecurePortEnabled: false 
    secureVirtualHostName: ${spring.application.name}
    homePageUrl: https://${eureka.instance.hostname}:${server.port}/
    statusPageUrl: https://${eureka.instance.hostname}:${server.port}/admin/info
    metadataMap:
      hostname : ${eureka.instance.hostname}
      securePort: ${server.port}
  client:
    serviceUrl:
      defaultZone: 'https://localhost:8760/eureka/'
    tls:
      enabled: true
      key-store: classpath:books.p12
      key-store-type: PKCS12
      key-store-password: mypass
      key-password: mypass
      trust-store: classpath:books.p12
      trust-store-type: PKCS12
      trust-store-password: mypass

For the discovery client side, there is one last thing to configure. Each service interacts with Eureka through a client RestTemplate component. We have to instruct the RestTemplate client to use SSL, and we can do it by providing a custom version of the bean in Spring Boot:

@SpringBootApplication
@RefreshScope
public class AppMain {

    @Value("${eureka.client.tls.trust-store}")
    private String trustStore;

    @Value("${eureka.client.tls.trust-store-password}")
    private String trustStorePassword;

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

    @Bean
    public RestTemplate restTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, 
                MalformedURLException,
                IOException {

        URL url = new URL(trustStore);
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(url, trustStorePassword.toCharArray())
            .build();
        SSLConnectionSocketFactory sslConFactory = new SSLConnectionSocketFactory(sslContext);
        HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
            .setSSLSocketFactory(sslConFactory)
            .build();
        CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(cm)
            .build();
        ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(requestFactory);
    }
}

Completing the Architecture with a Configuration Server

In the above sections, we explained how to configure the microservices and the discovery server to enforce secure communication through SSL protocol. We want to improve our architecture by providing a configuration server and storing services and the discovery server configuration on it. We also want to encrypt the passwords in the configuration files.

To implement a configuration server, we have to include the spring-cloud-config-server dependency, and the spring-boot-starter-security starter to enforce a basic user/password authentication and the encrypt/decrypt functionality.

	<dependencies>		
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>		
		<dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-security</artifactId>
		</dependency>
						
	</dependencies>

We should also annotate the main Spring Boot class with @EnableConfigServer:

@SpringBootApplication
@EnableConfigServer
public class AppMain {
    public static void main(String[] args) {
        SpringApplication.run(AppMain.class, args);
    }
}

To enable encrypt/decrypt functionality, we should also generate a key-store with a couple of public and private keys, as we did before for the micro-service and discovery server, and store it in the resources folder. Then we have to provide the following configuration in the application.yaml file.

server:
  port: ${PORT:8888}


spring:
  application:
    name: config-service
  security:
    user:
      name: myusername
      password: mypassword  
  cloud:
   config:
     server:
       git:
         uri: https://github.com/mcasari/spring-cloud-config-git-server-repo.git
         cloneOnStart: true

encrypt:
  keyStore:
    location: classpath:/config.p12
    password: mypass
    alias: config
    secret: mypass

To authenticate with the configuration server, we provide a simple username and password. The micro-service and discovery server configuration will be stored on git on the https://github.com/mcasari/spring-cloud-config-git-server-repo.git repository. The encrypt section defines the keystore and allows the server to expose an /encrypt endpoint that can be used to encrypt the passwords (or other sensitive information) to be stored in the configuration files.

We also have to disable the /decrypt endpoint, because this feature must be used only internally by the server to decrypt the configuration data. We do this by a SecurityFilterChain, denying access to the endpoint and permitting it for anything else:

@SpringBootApplication
@EnableConfigServer
public class AppMain {

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

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

		http.authorizeHttpRequests(auth -> auth.requestMatchers("/decrypt").denyAll().anyRequest().permitAll())
				.csrf(CsrfConfigurer::disable);

		return http.build();
	}

}

To encrypt a password we can do this in the command line:

curl localhost:8888/encrypt -s -d mypass

The encrypted password can then be included in the application.yaml file by prefixing it with {cipher}. You can see an example in the following fragment:

server:
  port: ${PORT:8092}
  ssl:
     enabled: true
     key-store: classpath:books.p12
     key-store-password: '{cipher}AQA0oPBAK1my1NcL3WUbl5LnJzDQ1bHEbmqhLyb0SRMqr7QWrPihDwjBxVMuWd8TD3Z47Dk1lJAdrvHUKBQEk3xfrPlHa2ioxZvaVYJhpUi84csdKNjHsbGclB9DNkGu90oVhUMuRxBTzjwcqA50KGUEQuxTYZ0zOye/kfUQ6THco7c3swUq6ni7VuB+d7KajBVct+yuND6o4tJ++WiJRWBsbEUqt/emO2kxRfmeTAFL3ExLvrLFmHT56KE4bEij1sDQBQucYH/oPL1Cmu+eiOMb91Kg4IAEfOZjSnlqFLWYDTZElghSx6WImxMUSmHc6zYRgICS6oQOI3zFIWeMqYomha4IcU0qx+pLMCwrYYrD99IM1bxL7y0nC2+x1SH4qbw='
     key-store-type: PKCS12
     trust-store: classpath:books.p12
     trust-store-password: '{cipher}AQA0oPBAK1my1NcL3WUbl5LnJzDQ1bHEbmqhLyb0SRMqr7QWrPihDwjBxVMuWd8TD3Z47Dk1lJAdrvHUKBQEk3xfrPlHa2ioxZvaVYJhpUi84csdKNjHsbGclB9DNkGu90oVhUMuRxBTzjwcqA50KGUEQuxTYZ0zOye/kfUQ6THco7c3swUq6ni7VuB+d7KajBVct+yuND6o4tJ++WiJRWBsbEUqt/emO2kxRfmeTAFL3ExLvrLFmHT56KE4bEij1sDQBQucYH/oPL1Cmu+eiOMb91Kg4IAEfOZjSnlqFLWYDTZElghSx6WImxMUSmHc6zYRgICS6oQOI3zFIWeMqYomha4IcU0qx+pLMCwrYYrD99IM1bxL7y0nC2+x1SH4qbw='

Conclusion

The first step in securing a micro-service system is to force the communication and mutual authentication of services through SSL protocol. It is also important to encrypt sensitive information in the configuration files hosted on the servers. In this article, we have seen a typical architecture with the configuration centralized in a configuration server, and the services interacting in protected mode through a discovery service.

You can find the source code of the example in this article on GitHub.

Primary Sidebar

Social

  • Facebook
  • LinkedIn
  • Twitter

Archives

  • May 2026
  • April 2026
  • March 2026
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • September 2023
  • May 2023
  • March 2023
  • January 2023
  • November 2022
  • September 2022
  • August 2022

Recent Posts

  • Better Avoid Spring Boot Lazy-initialization @PostConstruct Trap
  • How To Handle Large Datasets in Spring Boot
  • How to Log HTTP Incoming Requests in Spring Boot
  • How to Reliably Implement Post-Commit Actions in Spring
  • Brief and Easy Explanation of HATEOAS

TAGS

Algorithms Apache POI Backtracking Date Structures Dynamic Programming engineering Graphs Greedy Horror Stories HSSF Java Java New Features Java Principles Java tips real life Sorting Spring Boot Spring Cloud Strings Trees

Footer

Privacy Policy Cookie Policy Terms and Conditions