Backend Development 9 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Secure Spring Boot Configs: Encrypt Sensitive Properties with EnvironmentPostProcessor

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>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
  &lt;artifactId&gt;spring-cloud-context&lt;/artifactId&gt;
  &lt;version&gt;3.0.5&lt;/version&gt;
&lt;/dependency&gt;</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.

JavaconfigurationSpring BootEncryptionJCEEnvironmentPostProcessor
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.