Create your own
Lesson illustration

Securing Configuration Properties with Symmetric Encryption

Hello! Welcome to our lesson on securing sensitive configuration data.

In the previous lesson, we explored using dedicated external systems like HashiCorp Vault and AWS Secrets Manager to handle secrets. This is a robust, best-practice approach. However, there's another common pattern you'll encounter, especially in ecosystems that heavily leverage configuration-as-code stored in Git.

Today, we'll address this alternative strategy. Instead of storing secrets externally, we'll store them within our configuration repository, but in an encrypted format. Our goal is to learn how to implement symmetric encryption for sensitive properties in a configuration repository and configure clients for decryption. This is a practical skill and a frequent topic in technical interviews, as it demonstrates your ability to balance security with operational convenience in a GitOps workflow.

1. The Core Concept: Symmetric Encryption

The foundation of this approach is symmetric-key cryptography. Unlike asymmetric cryptography (which uses a public/private key pair), symmetric encryption uses the same secret key to both encrypt and decrypt data.

Symmetric Encryption Process
This diagram illustrates the process of symmetric encryption. A single secret key is used with an encryption algorithm to convert plaintext into ciphertext. The same secret key is then used with a decryption algorithm to convert the ciphertext back into the original plaintext.

In our context:

  1. A sensitive value, like a database password (my-secret-password), is the plaintext.
  2. We use a secret key (e.g., a-very-secret-phrase) and an encryption algorithm to turn it into ciphertext (e.g., aBcDeF123...).
  3. This ciphertext is what gets stored in our application.properties or application.yml file and committed to Git.
  4. At runtime, the application needs access to the same secret key to decrypt the ciphertext back into the original password before it can be used.

The most critical aspect of this entire process is the management of the secret key. This key must never be committed to the repository. It is the "master secret" and must be supplied to the application securely at runtime, typically through environment variables, a Kubernetes Secret, or a similar mechanism discussed in our previous lesson.

2. Approach 1: Server-Side Encryption with Spring Cloud Config

A very common architecture in Spring-based microservices involves a centralized Spring Cloud Config Server. This server is responsible for managing configuration for all other microservices, usually by reading from a Git repository.

Spring Cloud Config Server with Secure Configuration Sources
This diagram shows a typical Spring Cloud Config setup. The Config Server fetches configuration from a source like a Git repository. Client microservices (like the User Service and Order Service) then connect to the Config Server to get their properties, rather than reading them directly.

Spring Cloud Config has built-in support for property encryption. The workflow is as follows:

  • The Config Server is configured with a symmetric key.
  • It exposes secure endpoints (/encrypt and /decrypt).
  • A developer can POST a secret value to the /encrypt endpoint to get the encrypted ciphertext.
  • The developer commits this ciphertext, prefixed with {cipher}, to the property file in the Git repository (e.g., spring.datasource.password: {cipher}AgA...).
  • When a microservice client requests its configuration, the Config Server fetches the properties from Git, sees the {cipher} prefix, decrypts the value on the server side, and sends the plain-text, decrypted password to the client.

The beauty of this approach is its transparency. The client microservices are completely unaware that encryption is happening; they just receive the properties they need.

Encryption and Decryption :: Spring Cloud Config

The official Spring Cloud Config documentation provides the most authoritative explanation of this feature. Let's start there.

Please read the introduction and the subsequent section that demonstrates using the /encrypt and /decrypt endpoints. You can find this by looking for the curl command examples. Focus on understanding: The {cipher} prefix convention for marking encrypted values. The role of the /encrypt endpoint in generating the encrypted string. The key concept that the server decrypts the value before sending it to the client application.

To enable this on the server, you simply need to configure the key.

Quick Intro to Spring Cloud Configuration

The Baeldung article 'Quick Intro to Spring Cloud Configuration' offers a clear, step-by-step guide on how to set up this symmetric key on the server.

Please read the beginning of subsection 7.2. Key Management. Focus on the simple method for symmetric cryptography where it describes setting the encrypt.key property in the Config Server's application.properties. We will ignore the asymmetric (keystore) part for now.

As the articles show, you can enable symmetric encryption by setting encrypt.key=your-super-secret-key in the Config Server's properties. In a production environment, this key would be passed securely, for example, as an environment variable: ENCRYPT_KEY=your-super-secret-key.

3. Approach 2: Client-Side Decryption with Jasypt

What if you're not using Spring Cloud Config, or you want to avoid the operational overhead of running another service? You can implement decryption directly within your microservice. The most popular library for this in the Spring ecosystem is Jasypt (Java Simplified Encryption).

The workflow with Jasypt is:

  • Add the jasypt-spring-boot-starter dependency to your microservice's pom.xml.
  • Encrypt your secret properties. You can do this with a provided Maven plugin.
  • Place the encrypted value in your application.properties file, typically wrapped in ENC(...), for instance: spring.datasource.password: ENC(u3C... ).
  • Provide the master encryption key (Jasypt calls it a "password") to the application when it starts. This is usually done via an environment variable or system property, e.g., -Djasypt.encryptor.password=your-super-secret-key.
  • When the Spring application context loads, Jasypt's auto-configuration intercepts the property-loading process. It finds any encrypted values, decrypts them using the key you provided, and then passes the plain-text value to the rest of the Spring framework.

From your application code's perspective (@Value("${spring.datasource.password}")), it receives the decrypted value transparently.

Spring Boot Password Encryption for Application using Jasypt | JavaTechie

For a practical walkthrough of implementing client-side encryption using Jasypt, this video from Java Techie is excellent. It demonstrates the exact steps needed in a Spring Boot project.

Please watch the video from the beginning up to 06:41. Pay close attention to these key steps: 00:00 - 01:33: The problem statement: why we need to encrypt properties at rest. 01:33 - 02:48: Adding the necessary Jasypt Maven dependency and plugin to pom.xml. 02:48 - 06:41: Using the Maven plugin to encrypt a password, placing the encrypted value into application.properties, and providing the secret key to the application as a JVM argument so it can decrypt the property at startup.

4. Comparison: Server-Side vs. Client-Side Decryption

In an interview, you'll be expected to articulate the trade-offs between these two approaches. The "right" choice depends on the system's architecture and operational constraints.

AspectSpring Cloud Config (Server-Side)Jasypt (Client-Side)
InfrastructureRequires a running Config Server instance.No extra infrastructure needed; it's just a library in your app.
ComplexityCentralized in the Config Server. Client apps are simple.Decentralized. Logic is embedded in each microservice.
Key ManagementKey is managed in one place (the Config Server).Key must be securely distributed to every single instance of every microservice.
Key RotationEasy. Update the key on the Config Server and re-encrypt properties. Clients get new values on refresh.Harder. Requires updating the key and restarting all application instances across the entire fleet.
Blast RadiusConfig Server is a potential single point of failure. If it's down, clients may not start/refresh.A misconfiguration or bad key affects only one service. No single point of failure for decryption.
Best ForLarge ecosystems where centralized control and easy key rotation are paramount.Smaller projects or when you want to avoid the dependency on a central config server.
Test your understanding!

You are the lead architect for a system with 50+ microservices. Your company's security policy, enforced by the CISO (Chief Information Security Officer), mandates that all encryption keys for configuration data must be rotated every 90 days with zero downtime.

Which approach—server-side (Spring Cloud Config) or client-side (Jasypt)—would you recommend, and why? Justify your choice based on the key rotation requirement.

Show answer

The server-side (Spring Cloud Config) approach is the clear winner here.

Justification: The core requirement is easy, zero-downtime key rotation at scale (50+ services).

  • With Spring Cloud Config, you can rotate the key in a single location: the Config Server. You would update the encrypt.key, re-encrypt the properties in your Git repository, and the clients would pick up the new, decrypted values on their next configuration refresh. This process can be automated and managed centrally without restarting all 50+ microservices.
  • With Jasypt, key rotation would be an operational nightmare. You would need to update the jasypt.encryptor.password environment variable (or equivalent) for every single running instance of all 50+ services and then perform a rolling restart of the entire application fleet. This is slow, error-prone, and much harder to execute with a zero-downtime guarantee.

Therefore, for large-scale systems with strict security policies like mandatory key rotation, centralization is a major advantage.

Conclusion

You've now learned a powerful and common technique for securing secrets within a configuration repository. This method allows you to embrace configuration-as-code and GitOps practices without exposing sensitive data in your version control history.

Key Takeaways:

  • Encrypting properties in Git is a valid alternative to using external secret stores. The fundamental principle is to separate the encrypted data (in Git) from the decryption key (in the runtime environment).
  • Server-Side Decryption (Spring Cloud Config) centralizes the encryption logic and key management, making it easy to manage at scale but introducing a dependency on the Config Server.
  • Client-Side Decryption (Jasypt) is a lightweight, decentralized approach that embeds decryption logic into each microservice, avoiding extra infrastructure but making key distribution and rotation more complex.
  • In any interview discussion, being able to clearly articulate the trade-offs between these two patterns will demonstrate your depth of understanding.

Next Up

We've covered securing secrets at rest (in the repository) and how applications can securely access them. In our final lesson of this module, we will shift our focus back to communication. We will explore how to secure the transport layer itself for service-to-service calls, ensuring both confidentiality and mutual authentication, by diving into mutual TLS (mTLS).

Can't find a good explanation? Sign up and we'll make it for you

Sign up