Secure Your Spring Boot Apps: Easy Config and Field Encryption with Jasypt

This guide explains why sensitive configuration and user data must be encrypted in Spring Boot projects, demonstrates how to use Jasypt for property‑level encryption and custom AOP‑based field masking, and dives into the underlying PBE algorithm and source‑code mechanics.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Secure Your Spring Boot Apps: Easy Config and Field Encryption with Jasypt

Configuration Encryption with Jasypt

Jasypt provides symmetric (single‑key) and asymmetric encryption utilities for Java applications. To encrypt configuration values in a Spring Boot project, add the jasypt-spring-boot-starter dependency:

<dependency>
  <groupId>com.github.ulisesbocchio</groupId>
  <artifactId>jasypt-spring-boot-starter</artifactId>
  <version>2.1.0</version>
</dependency>

Define the master password (the secret used for all encrypt/decrypt operations). In application.yml or application.properties: jasypt.encryptor.password=yourSecretPassword Replace any sensitive value with the ENC(...) wrapper, for example:

spring.datasource.password=ENC(mVTvp4IddqdaYGqPl9lCQbzM3H/b0B6l)

Encrypted strings can be generated programmatically:

@Autowired
private StringEncryptor stringEncryptor;

public String encrypt(String plain) {
    return stringEncryptor.encrypt(plain);
}

For production, do not hard‑code the master password. Supply it via a JVM argument (e.g. -Djasypt.encryptor.password=MySecret) or retrieve it from a secure configuration center.

Sensitive Field Masking with AOP

Define two custom annotations to mark fields and methods that require encryption/decryption:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField { String[] value() default ""; }

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod { String type() default "ENCRYPT"; }

Implement an Aspect that intercepts methods annotated with @EncryptMethod, encrypts parameters annotated with @EncryptField, and decrypts return values:

@Aspect
@Component
public class EncryptHandler {
    @Autowired
    private StringEncryptor stringEncryptor;

    @Pointcut("@annotation(com.example.annotation.EncryptMethod)")
    public void encryptPointcut() {}

    @Around("encryptPointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // encrypt input parameters
        Object[] args = pjp.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof String) {
                args[i] = stringEncryptor.encrypt((String) args[i]);
            } else {
                // handle objects containing @EncryptField fields (omitted for brevity)
            }
        }
        // proceed with encrypted arguments
        Object result = pjp.proceed(args);
        // decrypt return value if needed
        if (result instanceof String) {
            return stringEncryptor.decrypt((String) result);
        }
        return result;
    }
}

Example controller using the annotations:

@EncryptMethod
@PostMapping("/test")
@ResponseBody
public Object testEncrypt(@RequestBody UserVo user, @EncryptField String name) {
    return insertUser(user, name);
}

@Data
public class UserVo implements Serializable {
    private Long userId;
    @EncryptField private String mobile;
    @EncryptField private String address;
    private String age;
}

The aspect encrypts the incoming mobile and address fields while leaving the response unchanged, achieving real‑time data masking.

How Jasypt Works Internally

The starter registers EnableEncryptablePropertiesBeanFactoryPostProcessor, which wraps each PropertySource with EncryptablePropertySourceWrapper. The wrapper overrides getProperty(String name) and detects the ENC(...) pattern. When such a value is requested, the wrapper decrypts it using Jasypt’s PBE implementation.

Encryption uses Password‑Based Encryption (PBE) from the JDK. The default algorithm is PBEWITHMD5ANDDES, which combines the user‑supplied password, an 8‑byte random salt, and a configurable iteration count.

public byte[] encrypt(byte[] message) {
    SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
    byte[] salt = saltGenerator.generateSalt(8);
    PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterations);
    SecretKey key = factory.generateSecret(keySpec);
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] params = cipher.getParameters().getEncoded();
    byte[] encrypted = cipher.doFinal(message);
    return ByteBuffer.allocate(1 + params.length + encrypted.length)
                     .put((byte) params.length)
                     .put(params)
                     .put(encrypted)
                     .array();
}

public byte[] decrypt(byte[] encryptedMessage) {
    int paramsLength = Byte.toUnsignedInt(encryptedMessage[0]);
    byte[] params = new byte[paramsLength];
    byte[] cipherText = new byte[encryptedMessage.length - paramsLength - 1];
    System.arraycopy(encryptedMessage, 1, params, 0, paramsLength);
    System.arraycopy(encryptedMessage, paramsLength + 1, cipherText, 0, cipherText.length);
    SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
    PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
    SecretKey key = factory.generateSecret(keySpec);
    AlgorithmParameters algParams = AlgorithmParameters.getInstance(algorithm);
    algParams.init(params);
    Cipher cipher = Cipher.getInstance(algorithm);
    cipher.init(Cipher.DECRYPT_MODE, key, algParams);
    return cipher.doFinal(cipherText);
}

Because a random salt is generated for each encryption, identical plaintexts produce different ciphertexts, enhancing security.

PBE Algorithm Details

Jasypt’s default algorithm PBEWITHMD5ANDDES is a wrapper around standard JDK cryptographic primitives. It combines:

Message‑digest algorithm MD5 for key derivation.

Symmetric cipher DES (or RC2) for the actual encryption.

An 8‑byte random salt to prevent pre‑computation attacks.

A configurable iteration count that slows down brute‑force attempts.

The password supplied by the developer is not used directly as a key; instead, the password, salt, and iteration count are processed by PBEKeySpec to derive a secret key. The random salt ensures that encrypting the same plaintext twice yields different ciphertexts. During decryption, the salt is extracted from the leading bytes of the stored ciphertext, the same key is regenerated, and the original plaintext is recovered.

Practical Takeaways

Store configuration secrets encrypted with Jasypt and keep the master password out of source control (use -D JVM arguments or a secret manager).

Use custom AOP annotations ( @EncryptField, @EncryptMethod) to automatically mask sensitive fields at method boundaries.

Understanding Jasypt’s PBE workflow allows you to switch algorithms, adjust salt size or iteration count for stronger protection.

Full example code and a runnable demo are available at the following repository URL: https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot-jasypt

JavaAOPSpring BootJasyptConfiguration EncryptionField MaskingPBE
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

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.