Jasypt๋ฅผ ์ด์šฉํ•œ Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ • ์ •๋ณด ์•”ํ˜ธํ™” ํ•˜๊ธฐ

Spring Boot์˜ application.yml(.properties)๊ณผ ๊ฐ™์€ ์„ค์ • ์ •๋ณด ํŒŒ์ผ์—๋Š” ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ •๋ณด๋‚˜ API ํ‚ค, ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ํ‰๋ฌธ์œผ๋กœ ์„ค์ •ํ•˜๊ฒŒ ๋˜๋ฉด ๊ฐ„ํ˜น ์ •๋ณด๊ฐ€ ๋…ธ์ถœ๋˜์—ˆ์„ ๋•Œ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๋ฅผ ์ดˆ๋ž˜ ํ•  ์ˆ˜ ์žˆ๋‹ค. Jasypt ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ค์ • ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ spring์—์„œ Jasypt ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•ด ๋ณด๊ณ ์ž ํ•œ๋‹ค.

Jasypt for Spring boot

Jasypt๋Š” Spring boot 2.X ๋ฒ„์ „๊ณผ 3.X ๋ฒ„์ „๊ณผ ํ†ตํ•ฉ์„ ์ง€์›ํ•œ๋‹ค. ์ƒ์„ธํ•œ ๋ฌธ์„œ๋Š” jasypt-spring-boot git์„ ์ฐธ๊ณ ํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.
ํ”„๋กœ์ ํŠธ์— jasypt-spring-boot์„ ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” 3๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. @SpringbootApplication ํ˜น์€ @EnableAutoConfiguration ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•˜๋Š” ๊ฒฝ์šฐ jasypt-spring-boot-starter ๋””ํŽœ๋˜์‹œ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ์ „์ฒด spring environment์—์„œ ์•”ํ˜ธํ™” ๊ฐ€๋Šฅํ•œ ์†์„ฑ์„ ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. jasypt-spring-boot(jasypt-spring-boot-starter ์•„๋‹˜) ๋””ํŽœ๋˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  @Configuration ํด๋ž˜์Šค์— @EnableEncryptableProperties ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ „์ฒด spring environment์—์„œ ์•”ํ˜ธํ™” ๊ฐ€๋Šฅํ•œ ์†์„ฑ์„ ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.
  3. jasypt-spring-boot ๋””ํŽœ๋˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฐœ๋ณ„ ์•”ํ˜ธํ™” ๊ฐ€๋Šฅํ•œ ํ”„๋กœํผํ‹ฐ ์†Œ์Šค๋ฅผ @EncryptablePropertySource์— ์ง€์ •ํ•˜์—ฌ ํŠน์ • ์„ค์ •ํŒŒ์ผ์— ๋Œ€ํ•ด์„œ ์•”ํ˜ธํ™” ๊ฐ€๋Šฅํ•œ ์†์„ฑ์„ ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

jasypt-spring-boot-starter ๋””ํŽœ๋˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ž๋™์œผ๋กœ jasypt-spring-boot ๋””ํŽœ๋˜์‹œ๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค.

jasypt for spring boot dependency

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

@SpringBootApplication ํ˜น์€ @EnableAutoConfiguration ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์œ„ ๋””ํŽœ๋˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ชจ๋“  ์‹œ์Šคํ…œ ์†์„ฑ, ํ™˜๊ฒฝ ์†์„ฑ, ๋ช…๋ น์ค„ ์ธ์ˆ˜, application.properties, application-*.properties[.yml] ์˜ ์†์„ฑ์— ์•”ํ˜ธํ™”๋œ ์†์„ฑ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค.
@SpringBootApplication ํ˜น์€ @EnableAutoConfiguration ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์•„๋ž˜ ๋””ํŽœ๋˜์‹œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  @Configuration ํด๋ž˜์Šค์— @EnableEncryptableProperties ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•˜์—ฌ ๋ชจ๋“  ์‹œ์Šคํ…œ ์†์„ฑ, ํ™˜๊ฒฝ ์†์„ฑ, ๋ช…๋ น์ค„ ์ธ์ˆ˜, application.properties, application-*.properties[.yml] ์˜ ์†์„ฑ์— ์•”ํ˜ธํ™”๋œ ์†์„ฑ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค.

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot</artifactId>
    <version>3.0.5</version>
</dependency>
XML
@Configuration
@EnableEncryptableProperties
public class MyApplication {
    ...
}
Java

@SpringBootApplication ํ˜น์€ @EnableAutoConfiguration ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํŠน์ • ์†์„ฑ ํŒŒ์ผ์— ๋Œ€ํ•ด์„œ๋งŒ ์•”ํ˜ธํ™”๋œ ์†์„ฑ์„ ํ™œ์„ฑํ™” ํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•œ๋‹ค.

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot</artifactId>
    <version>3.0.5</version>
</dependency>
XML
@Configuration
@EncryptablePropertySource(name = "EncryptedProperties", value = "classpath:encrypted.properties")
public class MyApplication {
    ...
}
Java

encrypted.properties ํŒŒ์ผ์— ๋Œ€ํ•ด์„œ๋งŒ ์•”ํ˜ธํ™”๋œ ์†์„ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

๋น„๋ฐ€๋ฒˆํ˜ธ ๊ธฐ๋ฐ˜์˜ ์•”ํ˜ธํ™” ์„ค์ • ๊ตฌ์„ฑ

Jasypt๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ธฐ๋ฐ˜์˜ ์•”ํ˜ธํ™”๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค. jasypt ์•”ํ˜ธํ™”๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ ์„ค์ •์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

KeyRequiredDefault Value
jasypt.encryptor.passwordTrue
jasypt.encryptor.algorithmFalsePBEWITHHMACSHA512ANDAES_256
jasypt.encryptor.key-obtention-iterationsFalse1000
jasypt.encryptor.pool-sizeFalse1
jasypt.encryptor.provider-nameFalseSunJCE
jasypt.encryptor.provider-class-nameFalsenull
jasypt.encryptor.salt-generator-classnameFalseorg.jasypt.salt.RandomSaltGenerator
jasypt.encryptor.iv-generator-classnameFalseorg.jasypt.iv.RandomIvGenerator
jasypt.encryptor.string-output-typeFalsebase64
jasypt.encryptor.proxy-property-sourcesFalsefalse
jasypt.encryptor.skip-property-sourcesFalseempty list

jasypt.encryptor.password ํ•ญ๋ชฉ๋งŒ ํ•„์ˆ˜์ด๊ณ  ๋‚˜๋จธ์ง€ ์„ค์ • ํ•ญ๋ชฉ์€ ๋ชจ๋‘ ์˜ต์…˜์ด๋‹ค. ์„ค์ • ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„๋กœ๋Š” password ํ•ญ๋ชฉ๋งŒ ์„ค์ •ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๊ทธ๋ƒฅ ๋””ํดํŠธ๋กœ ์‚ฌ์šฉํ•ด๋„ ์ถฉ๋ถ„ํ•  ๊ฒƒ ๊ฐ™๋‹ค.

๋””ํดํŠธ encryptor ์‚ฌ์šฉ

@SpringBootApplication ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Auto Configuration์ด ์ ์šฉ๋˜์–ด StringEncryptor ๋นˆ์„ ์ž๋™ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. StringEncryptor ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๊ณ  ๊ตฌํ˜„์ฒด ํด๋ž˜์Šค๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

jasypt ์ง€์› encryptor

๋””ํดํŠธ StringEncryptor๋Š” DefaultLazyEncryptor ์ด๊ณ  jasypt.encryptor.password ๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.
์•„๋ž˜๋Š” ๋””ํดํŠธ StringEncryptor๋ฅผ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

package com.example.jpa.jasypt;

import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import org.assertj.core.api.Assertions;
import org.jasypt.encryption.StringEncryptor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
//@EnableAutoConfiguration ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ
//@EnableEncryptableProperties annotation ์„ ํ†ตํ•ด์„œ default encryptor ๋ฅผ ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
@EnableEncryptableProperties
//๋‚ด๋ถ€์ ์œผ๋กœ @ConfigurationProperties๋ฅผ ํ†ตํ•ด์„œ ์„ค์ •์ •๋ณด๋ฅผ ๋กœ๋“œ ํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ์‹œ์—๋Š”
//์•„๋ž˜ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•ด ์ค˜์•ผ ํ•œ๋‹ค.
@ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class)
public class DefaultJasyptTest {
    @Autowired
    private StringEncryptor stringEncryptor;

    @Test
    void default_jasypt_test() {
        String encrypted = stringEncryptor.encrypt("test");
        System.out.println("encrypted: " + encrypted);

        String decrypted = stringEncryptor.decrypt(encrypted);
        System.out.println("decrypted: " + decrypted);
        Assertions.assertThat(decrypted).isEqualTo("test");
    }
}
Java

jasypt.encryptor.password ์„ค์ •์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. (application.yml)

jasypt:
  encryptor:
    password: "test-password"
YAML

Custom Encryptor ์‚ฌ์šฉ

@Configuration ํด๋ž˜์Šค์—์„œ StringEncryptor ๋นˆ์„ ์ •์˜ํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ธฐ๋ณธ Encryptor๋Š” ๋ฌด์‹œ๋œ๋‹ค.

package com.example.jpa.config;

import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JasyptConfig {
    @Bean("jasyptEncryptor")
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        //์•”๋ณตํ˜ธํ™”์— ์‚ฌ์šฉ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ • (ํ•„์ˆ˜)
        config.setPassword("test-password");
        //์•„๋ž˜ ์„ค์ •์€ ๋ชจ๋‘ ์˜ต์…˜ (๋””ํดํŠธ๋กœ ์…‹ํŒ…ํ•จ)
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);
        return encryptor;
    }
}
Java

1.5 ๋ฒ„์ „๋ถ€ํ„ฐ Bean์— ์ด๋ฆ„์„ ํ•„์ˆ˜๋กœ ์ง€์ •ํ•˜๊ณ  jasypt.encryptor.bean=<๋นˆ์ด๋ฆ„>๊ณผ ๊ฐ™์ด ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
@Bean ์ด๋ฆ„์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด default ๋กœ ๋นˆ ์ด๋ฆ„์€ jasyptStringEncryptor๋กœ ์„ค์ •๋œ๋‹ค.
jasyptStringEncryptor ์ด๋ฆ„์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ด๋ฆ„์„ ์ง€์ •ํ•œ ๊ฒฝ์šฐ์—๋Š” jasypt.encryptor.bean=<๋นˆ์ด๋ฆ„> ์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.

application.yml ์„ค์ •์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

jasypt:
  encryptor:
    bean: jasyptEncryptor
YAML

์œ„ ์ƒ˜ํ”Œ์ฝ”๋“œ์— ์ง€์ •๋œ StringEncryptor ๋นˆ์„ ์‚ฌ์šฉํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์•˜๋‹ค.

package com.example.jpa.jasypt;

import com.example.jpa.config.JasyptConfig;
import org.assertj.core.api.Assertions;
import org.jasypt.encryption.StringEncryptor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = JasyptConfig.class)
public class CustomEncryptorTest {
    @Autowired
    private StringEncryptor jasyptEncryptor;

    @Test
    void custom_jasypt_test() {
        String encrypted = jasyptEncryptor.encrypt("test");
        System.out.println("encrypted: " + encrypted);

        String decrypted = jasyptEncryptor.decrypt(encrypted);
        System.out.println("decrypted: " + decrypted);
        Assertions.assertThat(decrypted).isEqualTo("test");
    }
}
Java

JasyptConfig์— ์ •์˜๋œ StringEncryptor ๋นˆ์€ PooledPBEStringEncryptor ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์ฃผ์ž…๋ฐ›์€ StringEncryptor ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋Š” PooledPBEStringEncryptor ์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

StringEncryptor ํด๋ž˜์Šค ๊ฐ์ฒด ๋””๋ฒ„๊น…

์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์•”ํ˜ธํ™”๋œ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

encrypted: vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv
Plaintext

๋ณตํ˜ธํ™” ์ฒ˜๋ฆฌ ํ…Œ์ŠคํŠธ

jasypt ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” prefix์™€ suffix ๋Š” ๊ฐ๊ฐ “ENC(“์™€ “)” ์ด๋‹ค.
์ฆ‰, field: ENC(์•”ํ˜ธํ™”๋ฐ์ดํ„ฐ)์™€ ๊ฐ™์ด ์„ค์ •ํ•œ๋‹ค.
์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด์„œ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ • ํ›„์— ์ž๋™์œผ๋กœ ๋ณตํ˜ธํ™” ์ฒ˜๋ฆฌ๊ฐ€ ๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์ž.
์šฐ์„  application.yml ํŒŒ์ผ์— ์•”ํ˜ธํ™” ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค.

encrypted:
  data: ENC(vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv)
YAML

์œ„ ์„ค์ •์€ ‘test’ ๋ฌธ์ž์—ด์„ ์•”ํ˜ธํ™”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•œ ๊ฒƒ์ด๋‹ค.
์•„๋ž˜๋Š” encrypted.data ์„ค์ •์„ @Value ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ decryptedData ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๊ณ  ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

package com.example.jpa.jasypt;

import com.example.jpa.config.JasyptConfig;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(
    initializers = ConfigDataApplicationContextInitializer.class,
    classes = JasyptConfig.class)
//@SpringBootApplication ํ˜น์€ @EnableAutoConfiguration์„ ์‚ฌ์šฉํ•˜๋Š”
//production ์ฝ”๋“œ์—์„œ๋Š” ์•„๋ž˜ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•  ํ•„์š”๋Š” ์—†๋‹ค.
@EnableEncryptableProperties
public class ReadJasyptConfigTest {
    @Value("${encrypted.data}")
    private String decryptedData;

    @Test
    void decryption_config_test() {
        Assertions.assertThat(decryptedData).isEqualTo("test");
        System.out.println("decrypted config: " + decryptedData);
    }
}
Java

์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ @SpringBootApplication ์–ด๋…ธํ…Œ์ด์…˜ ํ˜น์€ @EnableAutoConfiguration ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” production ์ฝ”๋“œ์—์„œ๋Š” @EnableEncryptableProperties ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•  ํ•„์š”๋Š” ์—†๋‹ค.
์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์ˆ˜ํ–‰ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

[main] INFO com.ulisesbocchio.jasyptspringboot.configuration.EnableEncryptablePropertiesBeanFactoryPostProcessor -- Post-processing PropertySource instances
[main] INFO com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter -- Skipping PropertySource configurationProperties [class org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource
[main] INFO com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter -- Converting PropertySource test [org.springframework.core.env.MapPropertySource] to EncryptableMapPropertySourceWrapper
[main] INFO com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter -- Converting PropertySource systemProperties [org.springframework.core.env.PropertiesPropertySource] to EncryptableMapPropertySourceWrapper
[main] INFO com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter -- Converting PropertySource systemEnvironment [org.springframework.core.env.SystemEnvironmentPropertySource] to EncryptableSystemEnvironmentPropertySourceWrapper
[main] INFO com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter -- Converting PropertySource random [org.springframework.boot.env.RandomValuePropertySource] to EncryptablePropertySourceWrapper
[main] INFO com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter -- Converting PropertySource Config resource 'class path resource [application.yml]' via location 'optional:classpath:/' [org.springframework.boot.env.OriginTrackedMapPropertySource] to EncryptableMapPropertySourceWrapper
[main] INFO com.ulisesbocchio.jasyptspringboot.filter.DefaultLazyPropertyFilter -- Property Filter custom Bean not found with name 'encryptablePropertyFilter'. Initializing Default Property Filter
[main] INFO com.ulisesbocchio.jasyptspringboot.resolver.DefaultLazyPropertyResolver -- Property Resolver custom Bean not found with name 'encryptablePropertyResolver'. Initializing Default Property Resolver
[main] INFO com.ulisesbocchio.jasyptspringboot.detector.DefaultLazyPropertyDetector -- Property Detector custom Bean not found with name 'encryptablePropertyDetector'. Initializing Default Property Detector
[main] INFO com.ulisesbocchio.jasyptspringboot.encryptor.DefaultLazyEncryptor -- Found Custom Encryptor Bean org.jasypt.encryption.pbe.PooledPBEStringEncryptor@22fba58c with name: jasyptEncryptor
decrypted config: test
Plaintext

์•”ํ˜ธํ™” ์„ค์ • ํ˜•์‹ ๋ณ€๊ฒฝํ•˜๊ธฐ

Jasypt์—์„œ ์•”ํ˜ธํ™” ์„ค์ • ํ˜•์‹์€ ‘ENC(์•”ํ˜ธํ™”๋ฐ์ดํ„ฐ)’ ์ด๋‹ค. ์ด ํ˜•์‹์„ custom ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

์„ค์ •์œผ๋กœ ๋ณ€๊ฒฝ

jasypt.encryptor.property.prefix, jasypt.encryptor.property.suffix ์„ค์ •์„ ํ†ตํ•ด์„œ ์•”ํ˜ธํ™” ์„ค์ • ํ•„๋“œ ๊ฐ’ ํ˜•์‹์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

jasypt:
  encryptor:
    property:
      prefix: "ENC@["
      suffix: "]"
    bean: jasyptEncryptor

encrypted:
  data: ENC@[vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv]
YAML

์œ„์™€ ๊ฐ™์ด ์„ค์ • ํ›„ ReadJasyptConfigTest ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋ณตํ˜ธํ™” ๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
๋งŒ์•ฝ ์„ค์ •์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ–ˆ๋‹ค๋ฉด

jasypt:
  encryptor:
    property:
      prefix: "ENC@["
      suffix: "]"
    bean: jasyptEncryptor

encrypted:
  data: ENC(vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv)
YAML

ReadJasyptConfigTest ์ˆ˜ํ–‰๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

org.opentest4j.AssertionFailedError:
expected: "test"
 but was: "ENC(vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv)"
Expected :"test"
Actual   :"ENC(vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv)"
Plaintext

์ฆ‰ encrypted.data ์˜ ์„ค์ • ๊ฐ’ ENC(…)๋ฅผ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋กœ ์ œ๋Œ€๋กœ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋œ๋‹ค.

EncryptablePropertyDetector ๋นˆ ์ƒ์„ฑ์œผ๋กœ ๋ณ€๊ฒฝ

EncryptablePropertyDetector ๋ฅผ ๊ตฌํ˜„ํ•œ ๋นˆ์„ ์ •์˜ํ•˜์—ฌ ์•”ํ˜ธํ™” ์„ค์ • ํ˜•์‹์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ Bean ์ด๋ฆ„์€ encryptablePropertyDetector ๋กœ ์ง€์ •ํ•˜๊ณ , ๋งŒ์•ฝ encryptablePropertyDetector๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š”
jasypt.encryptor.property.detector-bean: <๋นˆ ์ด๋ฆ„> ์™€ ๊ฐ™์ด ๋นˆ ์ด๋ฆ„์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค. StringEncryptor ๋นˆ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ jasypt.encryptor.bean์„ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์ธ๋ฐ ์ด๋Š” ์•„๋ž˜ ํด๋ž˜์Šค์™€ ๊ด€๋ จ์ด ์žˆ๋‹ค.

@Configuration
public class EncryptablePropertyResolverConfiguration {

    private static final String ENCRYPTOR_BEAN_PROPERTY = "jasypt.encryptor.bean";
    private static final String ENCRYPTOR_BEAN_PLACEHOLDER = String.format("${%s:jasyptStringEncryptor}", ENCRYPTOR_BEAN_PROPERTY);
    private static final String DETECTOR_BEAN_PROPERTY = "jasypt.encryptor.property.detector-bean";
    private static final String DETECTOR_BEAN_PLACEHOLDER = String.format("${%s:encryptablePropertyDetector}", DETECTOR_BEAN_PROPERTY);
    private static final String RESOLVER_BEAN_PROPERTY = "jasypt.encryptor.property.resolver-bean";
    private static final String RESOLVER_BEAN_PLACEHOLDER = String.format("${%s:encryptablePropertyResolver}", RESOLVER_BEAN_PROPERTY);
    private static final String FILTER_BEAN_PROPERTY = "jasypt.encryptor.property.filter-bean";
    private static final String FILTER_BEAN_PLACEHOLDER = String.format("${%s:encryptablePropertyFilter}", FILTER_BEAN_PROPERTY);

    private static final String ENCRYPTOR_BEAN_NAME = "lazyJasyptStringEncryptor";
    private static final String DETECTOR_BEAN_NAME = "lazyEncryptablePropertyDetector";
    private static final String CONFIG_SINGLETON = "configPropsSingleton";
    /** Constant <code>RESOLVER_BEAN_NAME="lazyEncryptablePropertyResolver"</code> */
    public static final String RESOLVER_BEAN_NAME = "lazyEncryptablePropertyResolver";
    /** Constant <code>FILTER_BEAN_NAME="lazyEncryptablePropertyFilter"</code> */
    public static final String FILTER_BEAN_NAME = "lazyEncryptablePropertyFilter";

    ...
    ...
    ...
}
Java

EncryptablePropertyDetector๋ฅผ implement ํ•œ ๋นˆ์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

package com.example.jpa.config;

import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyDetector;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Optional;

@Configuration
public class JasyptConfig {

    @Bean("jasyptEncryptor")
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        //์•”๋ณตํ˜ธํ™”์— ์‚ฌ์šฉ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ • (ํ•„์ˆ˜)
        config.setPassword("test-password");
        //์•„๋ž˜ ์„ค์ •์€ ๋ชจ๋‘ ์˜ต์…˜ (๋””ํดํŠธ๋กœ ์…‹ํŒ…ํ•จ)
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);
        return encryptor;
    }

    @Bean(name = "customDetector")
    public EncryptablePropertyDetector customDetector() {
        return new CustomEncryptablePropertyDetector();
    }

    private static class CustomEncryptablePropertyDetector implements EncryptablePropertyDetector {

        private static final String prefix = "ENC@[";
        private static final String suffix = "]";
        @Override
        public boolean isEncrypted(String property) {
            if (property == null) {
                return false;
            }
            final String trimmedValue = property.trim();
            return (trimmedValue.startsWith(prefix) &&
                trimmedValue.endsWith(suffix));
        }

        @Override
        public String unwrapEncryptedValue(String property) {
            return Optional.ofNullable(property)
                .map(value -> value.substring(
                    prefix.length(),
                    (value.length() - suffix.length())))
                .orElse("");
        }
    }
}
Java

customDetector ์ด๋ฆ„์˜ ๋นˆ์„ ์ •์˜ํ•˜์˜€๋‹ค. customDetector ๋นˆ์€ ์„ค์ • ํ•„๋“œ์˜ ์•”ํ˜ธํ™” ํ˜•์‹์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ์ฐพ๋Š”๋ฐ ์‚ฌ์šฉ๋˜์—ˆ๋˜ DefaultPropertyDetector๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ์‚ฌ์šฉ๋œ๋‹ค. isEncrypted ๋Š” ์„ค์ • ๊ฐ’์ด ์•”ํ˜ธํ™” ํ˜•์‹์ด ๋งž๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๊ณ  unwrapEncryptedValue ๋Š” prefix, suffix ๋ฅผ ์ œ์™ธํ•œ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜์—ฌ ๋ฆฌํ„ดํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.
application.yml ์„ค์ •์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

jasypt:
  encryptor:
    property:
      detector-bean: customDetector
    bean: jasyptEncryptor

encrypted:
  data: ENC@[vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv]
YAML

Jasypt ํ”„๋กœํผํ‹ฐ ๋ณตํ˜ธํ™” ๋™์ž‘ ๋ฐฉ์‹

@EnableAutoConfiguration ์–ด๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋œ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ JasyptSpringBootAutoConfiguration ํด๋ž˜์Šค๊ฐ€ ์ฃผ์ž…๋˜๋ฉฐ ํ•ด๋‹น Configuration์€ @Import(EnableEncryptablePropertiesConfiguration.class) ๋ฅผ Import ํ•˜๋Š”๋ฐ EnableEncryptablePropertiesConfiguration ํด๋ž˜์Šค๋Š” EncrytablePropertyResolverConfiguration ํด๋ž˜์Šค๋ฅผ Import ํ•œ๋‹ค.
EncryptablePropertyResolverConfiguration ํด๋ž˜์Šค๋Š” EncryptablePropertySourceConverter ๋นˆ๊ณผ ์‚ฌ์šฉ์ž ์ •์˜ StringEncryptor ๋นˆ๊ณผ ๊ด€๋ จ๋œ ๋นˆ์„ ์ƒ์„ฑํ•œ๋‹ค. EncryptablePropertySourceConverter ๋นˆ์€ BeanFactoryPostProcessor๋ฅผ ๊ตฌํ˜„ํ•œ EnableEncryptablePropertiesBeanFactoryPostProcessor ๋นˆ์— ์ฃผ์ž…๋œ๋‹ค.
EnableEncryptablePropertiesBeanFactoryPostProcessor๋Š” postProcessBeanFactory ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ ์•ˆ์—์„œ PropertySource ๊ฐ์ฒด ํƒ€์ž…์„ EncryptableMapPropertySourceWrapper ํƒ€์ž…์œผ๋กœ convert๋ฅผ ํ•˜๊ณ  EncryptableMapPropertySourceWrapper๋ฅผ ํ†ตํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒ์„ฑํ•œ StringEncryptor ๋นˆ ํ˜น์€ ๋””ํดํŠธ StringEncryptor ๋นˆ์„ ํ†ตํ•ด์„œ ๋ณตํ˜ธํ™” ์ฒ˜๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง„๋‹ค.
์•„๋ž˜๋Š” Jasypt ์˜ JasyptSpringBootAutoConfiguration ํด๋ž˜์Šค ๋‚ด์šฉ์ด๋‹ค.

/**
 * <p>JasyptSpringBootAutoConfiguration class.</p>
 *
 * @author Ulises Bocchio
 * @version $Id: $Id
 */
@Configuration
@Import(EnableEncryptablePropertiesConfiguration.class)
public class JasyptSpringBootAutoConfiguration {
}
Java

์•„๋ž˜๋Š” EnableEncryptablePropertiesConfiguration ํด๋ž˜์Šค ๋‚ด์šฉ์ด๋‹ค.

@Configuration
@Import({EncryptablePropertyResolverConfiguration.class, CachingConfiguration.class})
@Slf4j
public class EnableEncryptablePropertiesConfiguration {

    /**
     * <p>enableEncryptablePropertySourcesPostProcessor.</p>
     *
     * @param environment a {@link org.springframework.core.env.ConfigurableEnvironment} object
     * @param converter a {@link com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter} object
     * @return a {@link com.ulisesbocchio.jasyptspringboot.configuration.EnableEncryptablePropertiesBeanFactoryPostProcessor} object
     */
    @Bean
    public static EnableEncryptablePropertiesBeanFactoryPostProcessor enableEncryptablePropertySourcesPostProcessor(final ConfigurableEnvironment environment, EncryptablePropertySourceConverter converter) {
        return new EnableEncryptablePropertiesBeanFactoryPostProcessor(environment, converter);
    }
}
Java

์œ„ Configuration ํŒŒ์ผ์—์„œ BeanFactoryPostProcessor๋ฅผ ๊ตฌํ˜„ํ•œ EnableEncryptablePropertiesBeanFactoryPostProcessor ๋นˆ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
@EnableEncryptableProperties ์–ด๋…ธํ…Œ์ด์…˜ ์—ญ์‹œ @Import(EnableEncryptablePropertiesConfiguration.class) ๋ฅผ Import ํ•˜์—ฌ ์œ„ ์„ค๋ช…๊ณผ ๋™์ผํ•œ ๋กœ์ง์œผ๋กœ ์•”ํ˜ธํ™” ๋œ ์„ค์ • ๋ฐ์ดํ„ฐ๋ฅผ ๋ณตํ˜ธํ™”๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.
์ด๋•Œ ๋ณตํ˜ธํ™”์— ์‚ฌ์šฉ๋˜๋Š” StringEncryptor ๋Š” Configuration ์œผ๋กœ ์ƒ์„ฑํ•œ StringEncryptor ๋นˆ์ด ๋ณตํ˜ธํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.
์•„๋ž˜๋Š” @EnableEncryptableProperties ์–ด๋…ธํ…Œ์ด์…˜ ํŒŒ์ผ ๋‚ด์šฉ์ด๋‹ค.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(EnableEncryptablePropertiesConfiguration.class)
public @interface EnableEncryptableProperties {
}
Java

์ง€๊ธˆ๊นŒ์ง€ Spring์—์„œ jasypt-spring-boot์„ ์ด์šฉํ•˜์—ฌ config ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„ ๋ณด์•˜๋‹ค. jasypt-spring-boot์˜ github ๋ฌธ์„œ์—๋Š” ๋” ๋งŽ์€ ๋‚ด์šฉ์ด ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ํ•ด๋„ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.


์ฐธ๊ณ ๋งํฌ

https://github.com/ulisesbocchio/jasypt-spring-boot</a >
http://www.jasypt.org/