# How to Encrypt and Decrypt Property Values Using the @EncryptedValue Annotation in Owner

> Secure sensitive property values in Owner with the @EncryptedValue annotation. Learn to implement Decryptor for transparent runtime decryption and protect your configuration.

- Repository: [Matteo Baccan/owner](https://github.com/matteobaccan/owner)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Mark sensitive property getters with `@EncryptedValue`, implement the `Decryptor` interface, and wire it via `@DecryptorClass` to enable transparent runtime decryption of encrypted configuration values.**

The Owner configuration library (matteobaccan/owner) protects sensitive data like passwords and API keys by letting you store encrypted values in property files and decrypt them automatically at runtime. This functionality centers on the **`@EncryptedValue`** annotation, which integrates with custom **`Decryptor`** implementations to handle cryptographic transformation transparently.

## Core Components of the Encryption System

Three elements work together to enable property encryption in Owner:

- **`@EncryptedValue` annotation** – Declared in [[`Config.java`](https://github.com/matteobaccan/owner/blob/main/Config.java) at lines 102-104](https://github.com/matteobaccan/owner/blob/master/owner/src/main/java/org/aeonbits/owner/Config.java#L102-L104), this method-level annotation marks properties requiring decryption. It accepts an optional `Class<? extends Decryptor>` parameter to specify a custom decryptor for that specific method.

- **`Decryptor` interface** – Defined in [[`Decryptor.java`](https://github.com/matteobaccan/owner/blob/main/Decryptor.java)](https://github.com/matteobaccan/owner/blob/master/owner/src/main/java/org/aeonbits/owner/Decryptor.java), this contract requires a single method `String decrypt(String value)` that transforms encrypted strings into clear text.

- **`@DecryptorClass` annotation** – Located in [[`Config.java`](https://github.com/matteobaccan/owner/blob/main/Config.java) at lines 108-115](https://github.com/matteobaccan/owner/blob/master/owner/src/main/java/org/aeonbits/owner/Config.java#L108-L115), this type-level annotation sets a default `Decryptor` for all methods in the configuration interface, which individual methods can override.

## Implementing a Custom Decryptor

Owner ships with `IdentityDecryptor`, a no-op implementation that returns values unchanged. For real encryption, extend `AbstractDecryptor` or implement `Decryptor` directly. The test suite provides a working AES example in [[`SampleDecryptor.java`](https://github.com/matteobaccan/owner/blob/main/SampleDecryptor.java)](https://github.com/matteobaccan/owner/blob/master/owner/src/test/java/org/aeonbits/owner/crypto/SampleDecryptor.java):

```java
public class SampleDecryptor extends AbstractEncryptor {
    private final StandardEncryptor encrypter;

    public SampleDecryptor(String algorithm, String secretKey) {
        this.encrypter = StandardEncryptor.newInstance(algorithm, secretKey);
    }

    @Override
    public String decrypt(String value) {
        return encrypter.decrypt(value);
    }

    @Override
    public String encrypt(String value) {
        return encrypter.encrypt(value);
    }
}

```

This implementation uses `StandardEncryptor` for AES encryption and returns Base64-encoded strings. Instantiate it with any algorithm and secret key your security policy requires.

## Wiring the Decryptor to Your Configuration

Attach a decryptor to an entire configuration interface using **`@DecryptorClass`**, then mark individual methods with **`@EncryptedValue`**:

```java
@DecryptorClass(AesDecryptor.class)
public interface SecureConfig extends Config {

    @Key("db.password")
    @EncryptedValue
    @DefaultValue("tzH7IKLCVc0AC72fh5DiZA==")
    String dbPassword();

    @Key("legacy.token")
    @EncryptedValue(LegacyDecryptor.class)  // overrides class-level default
    @DefaultValue("k9Jf...")
    String legacyToken();
}

```

In this example patterned after [[`CryptoConfigTest.java`](https://github.com/matteobaccan/owner/blob/main/CryptoConfigTest.java)](https://github.com/matteobaccan/owner/blob/master/owner/src/test/java/org/aeonbits/owner/crypto/CryptoConfigTest.java), the `dbPassword()` method uses the class-level `AesDecryptor`, while `legacyToken()` specifies its own decryptor via the annotation value attribute.

## How Runtime Decryption Works

When you invoke `config.dbPassword()`, Owner executes the following steps:

1. Retrieves the raw Base64 string from the underlying `Properties` source.
2. Detects the `@EncryptedValue` annotation on the method.
3. Resolves the `Decryptor` implementation using the precedence: method-level annotation value → type-level `@DecryptorClass` → `IdentityDecryptor`.
4. Invokes `decrypt()` on the resolved implementation.
5. Returns the clear-text result to your application code.

This process is completely transparent to calling code; the decryption happens automatically during property access.

## Encrypting Values for Your Configuration Files

Owner does not perform encryption automatically—you must generate encrypted strings beforehand and paste them into your `@DefaultValue` annotations or `.properties` files. Use your `Decryptor` implementation's encrypt method in a one-off utility:

```java
public class EncryptionUtility {
    public static void main(String[] args) {
        SampleDecryptor decryptor = new SampleDecryptor("AES", "ABCDEFGH12345678");
        String encrypted = decryptor.encrypt("MySuperSecretPassword");
        System.out.println(encrypted);  // Output: tzH7IKLCVc0AC72fh5DiZA==
    }
}

```

Run this utility locally or in your CI pipeline, then commit only the encrypted Base64 strings. The secret key remains in your decryptor implementation or environment configuration, never in the property files themselves.

## Complete Implementation Example

The following interface demonstrates a fully working encrypted configuration using components from the Owner source:

```java
package com.example.config;

import org.aeonbits.owner.Config;
import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.DecryptorClass;
import org.aeonbits.owner.EncryptedValue;
import org.aeonbits.owner.Key;
import org.aeonbits.owner.DefaultValue;

@DecryptorClass(MyAesDecryptor.class)
public interface DatabaseConfig extends Config {

    @Key("database.password")
    @EncryptedValue
    @DefaultValue("tzH7IKLCVc0AC72fh5DiZA==")
    String password();

    @Key("api.key")
    @EncryptedValue
    @DefaultValue("xJ9sLm2...")
    String apiKey();
}

```

Access decrypted values at runtime:

```java
DatabaseConfig config = ConfigFactory.create(DatabaseConfig.class);
System.out.println(config.password());  // prints: MySuperSecretPassword

```

## Summary

- Implement the **`Decryptor`** interface (or extend `AbstractDecryptor`) to define your cryptographic algorithm, as shown in [`SampleDecryptor.java`](https://github.com/matteobaccan/owner/blob/main/SampleDecryptor.java).
- Apply **`@DecryptorClass`** to your configuration interface to set a default decryptor for all encrypted properties.
- Mark sensitive getters with **`@EncryptedValue`**, optionally specifying a method-specific decryptor class.
- Generate encrypted values offline using your decryptor's encrypt method, then store the Base64 output in `@DefaultValue` or external property files.
- Owner automatically decrypts values at runtime when you access the configuration methods.

## Frequently Asked Questions

### What encryption algorithms does Owner support?

Owner itself is algorithm-agnostic. The encryption mechanism depends entirely on your `Decryptor` implementation. The test suite in [`CryptoConfigTest.java`](https://github.com/matteobaccan/owner/blob/main/CryptoConfigTest.java) demonstrates AES encryption via `StandardEncryptor`, but you can implement DES, RSA, or custom XOR schemes (as shown in [`EncryptedPropertiesExample.java`](https://github.com/matteobaccan/owner/blob/main/EncryptedPropertiesExample.java)) by writing the corresponding `decrypt()` logic.

### How do I handle different encryption keys for different properties?

Specify method-level decryptors using `@EncryptedValue(CustomDecryptor.class)`. Create separate `Decryptor` classes for each key or algorithm, then reference them individually on the relevant getter methods. This overrides the class-level `@DecryptorClass` annotation for that specific property.

### Is the secret key stored in the configuration files?

No. The encrypted values (Base64 ciphertext) reside in your property files or annotations, but the secret keys and algorithm specifications live in your `Decryptor` implementation classes or are loaded via environment variables. Keep your decryptor classes separate from your configuration interfaces to maintain security boundaries.

### Can I use @EncryptedValue with external properties files instead of @DefaultValue?

Yes. The `@EncryptedValue` annotation works with any property source Owner supports, including `.properties` files, system properties, or environment variables. Store the encrypted Base64 strings in your external files, mark the interface methods with `@EncryptedValue`, and Owner decrypts the values when accessed regardless of the source.