Secure Spring Boot Configs: Encrypt Sensitive Properties with EnvironmentPostProcessor
This guide explains how to protect plaintext Spring Boot configuration values by encrypting them using Spring Cloud Context's DecryptEnvironmentPostProcessor, covering dependency setup, key generation, JCE policy installation, code examples, and runtime decryption for secure property access.
Overview
Spring Boot configuration files often store sensitive data such as database passwords in plain text, which poses security risks. To enhance overall system security, these values should be encrypted so that even if the configuration files are exposed, the information remains protected.
Encryption Options
Two common approaches exist for encrypting sensitive configuration data in Spring Boot:
Jasypt – a powerful open‑source encryption library (not covered here).
EnvironmentPostProcessor – implement a custom processor or use the built‑in DecryptEnvironmentPostProcessor provided by the spring-cloud-context package.
Dependency
<code><dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>3.0.5</version>
</dependency></code>Application Configuration
<code>encrypt:
key: 123456789 # encryption key
salt: abcdef # salt for encryption
---
spring:
cloud:
decrypt-environment-post-processor:
enabled: true # enable decryption</code>Generating Encrypted Values
To encrypt a property such as custom.password , run the following Java code to produce a cipher text:
<code>public static void main(String[] args) throws Exception {
String key = "123456789";
String salt = "abcdef";
String text = "123123";
KeyProperties keyProperties = new KeyProperties();
keyProperties.setKey(key);
keyProperties.setSalt(salt);
String result = TextEncryptorUtils.createTextEncryptor(keyProperties, null).encrypt(text);
System.out.println(result);
}</code>The output should be placed in the configuration file with the required {cipher} prefix:
<code>custom:
password: "{cipher}2a483a44681006be6f0730b1acb45325c6bd20abe37369ef4fdb52c2e194a365"</code>Handling Illegal Key Size Errors
If the program throws an Illegal key size exception, install the appropriate Java Cryptography Extension (JCE) policy files for your JDK version (6, 7, or 8). After extracting the files to the JRE security directory, the encryption code will run successfully.
Testing Decrypted Values
<code>@Value("${custom.password}")
public String pwd;
@GetMapping("/pwd")
public String pwd() {
return pwd;
}</code>When the application starts, the endpoint /pwd returns the original plaintext password, confirming successful decryption.
Underlying Mechanism
The DecryptEnvironmentPostProcessor class is auto‑configured by the spring-cloud-context module. Its postProcessEnvironment method checks whether decryption is enabled, verifies the presence of TextEncryptor , and then decrypts all properties prefixed with {cipher} :
<code>public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt implements EnvironmentPostProcessor, Ordered {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (bootstrapEnabled(environment) || useLegacyProcessing(environment) || !isEnabled(environment)) {
return;
}
if (!ClassUtils.isPresent("org.springframework.security.crypto.encrypt.TextEncryptor", null)) {
return;
}
MutablePropertySources propertySources = environment.getPropertySources();
environment.getPropertySources().remove(DECRYPTED_PROPERTY_SOURCE_NAME);
Map<String, Object> map = TextEncryptorUtils.decrypt(this, environment, propertySources);
if (!map.isEmpty()) {
propertySources.addFirst(new SystemEnvironmentPropertySource(DECRYPTED_PROPERTY_SOURCE_NAME, map));
}
}
protected Boolean isEnabled(ConfigurableEnvironment environment) {
return environment.getProperty("spring.cloud.decrypt-environment-post-processor.enabled", Boolean.class, true);
}
}
</code>The TextEncryptorUtils class obtains the encryption key, salt, and optional RSA settings to create a TextEncryptor (AES by default) and performs the actual decryption.
<code>public abstract class TextEncryptorUtils {
static Map<String, Object> decrypt(AbstractEnvironmentDecrypt decryptor, ConfigurableEnvironment environment, MutablePropertySources propertySources) {
TextEncryptor encryptor = getTextEncryptor(decryptor, environment);
return decryptor.decrypt(encryptor, propertySources);
}
static TextEncryptor getTextEncryptor(AbstractEnvironmentDecrypt decryptor, ConfigurableEnvironment environment) {
Binder binder = Binder.get(environment);
KeyProperties keyProperties = binder.bind(KeyProperties.PREFIX, KeyProperties.class).orElseGet(KeyProperties::new);
if (keysConfigured(keyProperties)) {
decryptor.setFailOnError(keyProperties.isFailOnError());
if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null)) {
RsaProperties rsaProperties = binder.bind(RsaProperties.PREFIX, RsaProperties.class).orElseGet(RsaProperties::new);
return createTextEncryptor(keyProperties, rsaProperties);
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
}
return new FailsafeTextEncryptor();
}
}
</code>The abstract class AbstractEnvironmentDecrypt defines the decryption logic that replaces any property value starting with {cipher} with its decrypted counterpart.
<code>public abstract class AbstractEnvironmentDecrypt {
public static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
protected Map<String, Object> decrypt(TextEncryptor encryptor, PropertySources propertySources) {
Map<String, Object> properties = merge(propertySources);
decrypt(encryptor, properties);
return properties;
}
protected void decrypt(TextEncryptor encryptor, Map<String, Object> properties) {
properties.replaceAll((key, value) -> {
String valueString = value.toString();
if (!valueString.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
return value;
}
return decrypt(encryptor, key, valueString);
});
}
protected String decrypt(TextEncryptor encryptor, String key, String original) {
String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
try {
return encryptor.decrypt(value);
} catch (Exception e) {
return "";
}
}
}
</code>Overall, the process of encrypting and decrypting Spring Boot configuration properties using the built‑in EnvironmentPostProcessor is straightforward and provides a robust way to protect sensitive information.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.