최종 업데이트: 2026년 4월
application.yml이나 application.properties 파일을 열어보면 데이터베이스 비밀번호, API 키, 외부 서비스 인증 정보가 평문 그대로 들어 있는 프로젝트를 한 번쯤은 경험해 봤을 것이다. Git 저장소에 커밋된 그 순간부터 해당 비밀번호는 히스토리에 영구적으로 남게 되고, 저장소 접근 권한을 가진 모든 사람이 프로덕션 DB에 접속할 수 있는 상태가 된다. 실제로 GitHub에 노출된 AWS 키로 인해 수천만 원의 과금이 발생한 사례도 드물지 않다.
Jasypt(Java Simplified Encryption)는 이 문제를 가장 적은 비용으로 해결할 수 있는 라이브러리다. Spring Boot 환경에서는 jasypt-spring-boot-starter를 의존성에 추가하는 것만으로 설정 파일의 민감 정보를 암호화된 형태로 관리할 수 있다. 애플리케이션이 기동될 때 자동으로 복호화가 이루어지므로, 코드 변경 없이 기존 프로퍼티 참조 방식을 그대로 유지할 수 있다는 점이 핵심이다.
이 글에서는 jasypt-spring-boot v3.0.5 기준의 기본 사용법부터 Custom Encryptor 구성, 복호화 동작 원리까지 다루고, Spring Boot 3 + jasypt-spring-boot 4.x 마이그레이션 방법과 실무에서 자주 마주치는 트러블슈팅 사례까지 정리한다.
Jasypt Spring Boot 의존성 추가, 3가지 방법
Jasypt는 Spring boot 2.X 버전과 3.X 버전과 통합을 지원한다. 상세한 문서는 jasypt-spring-boot git을 참고하기 바란다.
프로젝트에 jasypt-spring-boot을 통합하는 방법에는 3가지가 있다.
- @SpringbootApplication 혹은 @EnableAutoConfiguration 어노테이션을 지정하는 경우 jasypt-spring-boot-starter 디펜던시만 추가하면 전체 spring environment에서 암호화 가능한 속성을 활성화 할 수 있다.
- jasypt-spring-boot(jasypt-spring-boot-starter 아님) 디펜던시를 추가하고 @Configuration 클래스에 @EnableEncryptableProperties 어노테이션을 추가하여 전체 spring environment에서 암호화 가능한 속성을 활성화 할 수 있다.
- jasypt-spring-boot 디펜던시를 추가하고 개별 암호화 가능한 프로퍼티 소스를 @EncryptablePropertySource에 지정하여 특정 설정파일에 대해서 암호화 가능한 속성을 활성화 할 수 있다.
jasypt-spring-boot-starter 디펜던시를 추가하면 자동으로 jasypt-spring-boot 디펜던시가 추가된다.
jasypt-spring-boot-starter 의존성 설정
<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 {
...
}Javaencrypted.properties 파일에 대해서만 암호화된 속성을 설정할 수 있도록 한다.
비밀번호 기반 암호화(PBE), 이렇게 구성한다
Jasypt는 비밀번호 기반의 암호화를 구성해야 한다. jasypt 암호화를 구성하기 위한 기본 설정은 아래와 같다.
| Key | Required | Default Value |
|---|---|---|
| jasypt.encryptor.password | True | – |
| jasypt.encryptor.algorithm | False | PBEWITHHMACSHA512ANDAES_256 |
| jasypt.encryptor.key-obtention-iterations | False | 1000 |
| jasypt.encryptor.pool-size | False | 1 |
| jasypt.encryptor.provider-name | False | SunJCE |
| jasypt.encryptor.provider-class-name | False | null |
| jasypt.encryptor.salt-generator-classname | False | org.jasypt.salt.RandomSaltGenerator |
| jasypt.encryptor.iv-generator-classname | False | org.jasypt.iv.RandomIvGenerator |
| jasypt.encryptor.string-output-type | False | base64 |
| jasypt.encryptor.proxy-property-sources | False | false |
| jasypt.encryptor.skip-property-sources | False | empty list |
jasypt.encryptor.password 항목만 필수이고 나머지 설정 항목은 모두 옵션이다. 설정 정보를 암호화하여 사용하기 위한 용도로는 password 항목만 설정하고 나머지는 그냥 디폴트로 사용해도 충분할 것 같다.
기본 Encryptor로 빠르게 시작하기
@SpringBootApplication 어노테이션을 사용하는 경우 Auto Configuration이 적용되어 StringEncryptor 빈을 자동 주입받아 사용할 수 있다. StringEncryptor 는 인터페이스이고 구현체 클래스는 아래와 같다.

디폴트 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");
}
}Javajasypt.encryptor.password 설정은 아래와 같다. (application.yml)
jasypt:
encryptor:
password: "test-password"YAMLCustom 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;
}
}Java1.5 버전부터 Bean에 이름을 필수로 지정하고 jasypt.encryptor.bean=<빈이름>과 같이 설정해야 한다.
@Bean 이름을 지정하지 않으면 default 로 빈 이름은 jasyptStringEncryptor로 설정된다.
jasyptStringEncryptor 이름이 아닌 다른 이름을 지정한 경우에는 jasypt.encryptor.bean=<빈이름> 을 설정해야 한다.
application.yml 설정은 아래와 같다.
jasypt:
encryptor:
bean: jasyptEncryptorYAML위 샘플코드에 지정된 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");
}
}JavaJasyptConfig에 정의된 StringEncryptor 빈은 PooledPBEStringEncryptor 객체를 생성한다. 위 테스트 코드에서 주입받은 StringEncryptor 클래스의 객체는 PooledPBEStringEncryptor 임을 확인할 수 있다.

위 테스트 코드에서 암호화된 결과는 아래와 같다.
encrypted: vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBvPlaintext암호화한 설정값, 정말 복호화되는지 테스트하기
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: testPlaintextENC() 접두사/접미사, 입맛대로 바꾸는 법
Jasypt에서 암호화 설정 형식은 ‘ENC(암호화데이터)’ 이다. 이 형식을 custom 하게 변경할 수 있다.
YAML 설정으로 변경하기
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)YAMLReadJasyptConfigTest 수행결과는 아래와 같이 오류가 발생하게 된다.
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";
...
...
...
}JavaEncryptablePropertyDetector를 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("");
}
}
}JavacustomDetector 이름의 빈을 정의하였다. customDetector 빈은 설정 필드의 암호화 형식인지 여부를 찾는데 사용되었던 DefaultPropertyDetector를 대신하여 사용된다. isEncrypted 는 설정 값이 암호화 형식이 맞는지 여부를 체크하는 메소드이고 unwrapEncryptedValue 는 prefix, suffix 를 제외한 암호화된 데이터를 추출하여 리턴하는 메소드이다.
application.yml 설정은 아래와 같다.
jasypt:
encryptor:
property:
detector-bean: customDetector
bean: jasyptEncryptor
encrypted:
data: ENC@[vwOUi946+YovW0IRw/O4ePs8JMpx/bhPGge+mnqVQP5W1VgNK9lCcE2s6YoWrdBv]YAMLJasypt는 내부적으로 어떻게 복호화할까?
@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 {
}JavaSpring Boot 3으로 넘어갈 때 Jasypt는 어떻게 따라가나
Spring Boot 3는 Jakarta EE 9+ 전환과 Java 17 최소 요구사항이라는 굵직한 변화를 가져왔다. jasypt-spring-boot도 이에 맞춰 4.x 버전 라인을 출시했다. 2025년 1월 기준 최신 버전은 4.0.4이며, Spring Boot 3.5.0 이상과 Java 17 이상을 요구한다. 기존에 3.0.4 이하 버전을 사용하던 프로젝트라면 의존성 버전만 올리는 것으로 끝나지 않을 수 있으므로, 아래 내용을 꼼꼼히 확인하는 것이 좋다.
버전 호환성 한눈에 보기
| jasypt-spring-boot | Spring Boot | Java | 비고 |
|---|---|---|---|
| 3.0.4 | 2.5.x ~ 2.7.x | 8+ | Spring Boot 2 계열 마지막 안정 버전 |
| 3.0.5 | 2.7.x ~ 3.1.x | 8+ (권장 17) | Spring Boot 3 초기 지원 추가 |
| 4.0.3 | 3.5.0+ | 17+ | Spring Boot 3.5 기반 재구성 |
| 4.0.4 | 3.5.0+ | 17+ | 최신 안정 버전 (2025.01) |
주의: v3.0.5는 Spring Boot 3.2 이상에서 호환성 문제가 보고되어 있다(GitHub #386). Spring Boot 3.2~3.4 구간을 사용 중이라면 4.0.4로 올리는 것이 안전하다.
의존성 변경은 이것만 바꾸면 된다
Maven 기준으로 pom.xml의 버전만 4.0.4로 올리면 된다. groupId와 artifactId는 동일하게 유지된다.
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>4.0.4</version>
</dependency>위 의존성은 기존 3.0.x 버전과 groupId, artifactId가 완전히 동일하다. 버전 숫자만 변경하면 되므로 Maven이나 Gradle 설정 자체의 변경 부담은 크지 않다. 다만, 프로젝트의 Java 버전이 17 미만이거나 Spring Boot가 3.5 미만이라면 먼저 프레임워크 업그레이드를 진행해야 한다.
Gradle을 사용하는 경우에도 마찬가지다.
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:4.0.4'Gradle Kotlin DSL이든 Groovy DSL이든 의존성 선언 형식만 다를 뿐, 변경해야 하는 좌표는 동일하다. 버전 카탈로그를 사용하는 프로젝트라면 libs.versions.toml의 jasypt 버전 항목만 수정하면 된다.
마이그레이션 시 놓치기 쉬운 체크리스트
javax → jakarta 패키지 전환 확인: jasypt-spring-boot 4.x 자체는 javax 패키지를 직접 참조하지 않지만, 커스텀 Encryptor를 Bean으로 등록할 때 javax.annotation 대신 jakarta.annotation을 사용해야 한다. @PostConstruct 같은 어노테이션의 import 경로가 바뀌었는지 확인한다.
Spring Cloud Bootstrap 변경: Spring Boot 3에서 bootstrap.yml 로딩 방식이 달라졌다. jasypt 설정을 bootstrap.yml에 넣어두던 프로젝트라면, spring.config.import 방식으로 전환하거나 spring-cloud-starter-bootstrap 의존성을 명시적으로 추가해야 한다.
프로퍼티 소스 우선순위: 4.x에서는 EncryptablePropertySource의 초기화 시점이 미세하게 변경되었다. 프로파일별로 다른 jasypt password를 사용하는 구성에서 간혹 복호화 시점 이슈가 발생할 수 있으므로, 업그레이드 후 각 프로파일 환경에서 기동 테스트를 반드시 수행한다.
Jasypt 설정하다 막혔을 때 — 자주 만나는 에러 4가지
Jasypt는 설정이 단순한 편이지만, 환경 차이나 사소한 실수로 인해 예상치 못한 에러를 마주칠 때가 있다. 아래는 실무에서 가장 빈번하게 발생하는 문제와 해결 방법이다.
1. EncryptionOperationNotPossibleException이 터지는 경우
이 예외는 “복호화에 실패했다”는 뜻인데, 원인이 딱 하나가 아니어서 디버깅이 까다롭다. 가장 흔한 원인 두 가지는 password 불일치와 알고리즘 미스매치다.
password 불일치: 암호화할 때 사용한 password와 애플리케이션 기동 시 전달하는 password가 다르면 이 예외가 발생한다. 특히 password에 특수문자가 포함된 경우, 셸 환경에서 이스케이프 처리가 되면서 실제 전달되는 값이 달라지는 경우가 많다.
# 특수문자 포함 시 작은따옴표 필수
export JASYPT_ENCRYPTOR_PASSWORD='my$ecret!Pass'
# 값이 정확히 전달되는지 확인
echo $JASYPT_ENCRYPTOR_PASSWORD위와 같이 작은따옴표로 감싸면 셸이 특수문자를 해석하지 않고 원본 그대로 전달한다. 큰따옴표 안에서는 $ 기호가 변수로 치환될 수 있으므로 작은따옴표를 사용하는 것이 안전하다.
알고리즘 미스매치: jasypt-spring-boot 3.0.x의 디폴트 알고리즘은 PBEWITHHMACSHA512ANDAES_256이다. 만약 암호화 시점에 다른 알고리즘(예: PBEWithMD5AndDES)으로 생성한 암호문을 이 디폴트 설정으로 복호화하려 하면 동일한 예외가 발생한다. jasypt.encryptor.algorithm 설정이 암호화할 때 사용한 알고리즘과 일치하는지 반드시 대조한다.
2. “Required Jasypt configuration not found” — Bean 이름 충돌
Custom Encryptor를 Bean으로 등록할 때 Bean 이름을 jasyptStringEncryptor로 지정하지 않으면 이 에러가 발생할 수 있다. jasypt-spring-boot-starter는 내부적으로 “jasyptStringEncryptor”라는 이름의 Bean을 찾아서 사용하기 때문이다.
// 올바른 Bean 이름 지정 — 기본값 "jasyptStringEncryptor" 사용
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(System.getenv("JASYPT_ENCRYPTOR_PASSWORD"));
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
encryptor.setConfig(config);
return encryptor;
}위 코드에서 @Bean 어노테이션에 “jasyptStringEncryptor”를 명시한 부분이 핵심이다. 이 이름을 생략하거나 다른 이름을 사용하면 starter가 커스텀 Bean을 인식하지 못하고 디폴트 Encryptor를 생성하려 시도한다. 만약 디폴트에 필요한 설정마저 없으면 에러로 이어진다. Bean 이름을 변경하고 싶다면 jasypt.encryptor.bean 프로퍼티로 커스텀 이름을 지정할 수도 있다.
3. Spring Boot 3 업그레이드 후 Jasypt가 동작하지 않는 경우
Spring Boot 2에서 3으로 올리면서 jasypt-spring-boot 버전을 3.0.4로 유지한 경우, 자동 설정(Auto Configuration)이 정상적으로 로드되지 않을 수 있다. Spring Boot 3에서는 META-INF/spring.factories 대신 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일을 사용하도록 변경되었기 때문이다.
이 문제를 해결하려면 jasypt-spring-boot-starter를 3.0.5 이상으로 올려야 한다. 3.0.5부터 Spring Boot 3의 새로운 자동 설정 로딩 방식을 지원한다. Spring Boot 3.5 이상을 사용한다면 4.0.4를 권장한다.
4. jasypt password를 안전하게 전달하는 세 가지 방법
jasypt의 암호화 키(password)를 application.yml에 평문으로 넣으면 암호화의 의미가 없어진다. 실무에서 사용하는 전달 방법은 크게 세 가지다.
JVM 시스템 프로퍼티: 가장 간단한 방법이다. 애플리케이션 기동 시 -D 옵션으로 전달한다.
java -Djasypt.encryptor.password=mySecretKey -jar app.jar이 방식은 로컬 개발이나 간단한 배포에 적합하다. 다만, ps 명령으로 프로세스 목록을 조회하면 JVM 인자가 노출될 수 있으므로 프로덕션 환경에서는 주의가 필요하다.
OS 환경변수: 프로세스 목록에 password가 노출되지 않는 장점이 있다.
# 환경변수 설정
export JASYPT_ENCRYPTOR_PASSWORD=mySecretKey환경변수 방식은 Spring Boot의 Relaxed Binding 덕분에 JASYPT_ENCRYPTOR_PASSWORD라는 이름이 자동으로 jasypt.encryptor.password 프로퍼티에 매핑된다. 별도로 application.yml에 명시하지 않아도 동작하지만, 명시적으로 작성해 두면 어떤 환경변수가 필요한지 팀원들이 파악하기 쉬워진다.
Docker 환경: 컨테이너 기반 배포에서는 docker run의 -e 옵션이나 docker-compose.yml의 environment 섹션을 활용한다.
# docker-compose.yml
services:
app:
image: my-spring-app:latest
environment:
- JASYPT_ENCRYPTOR_PASSWORD=${JASYPT_PASSWORD}
env_file:
- .env # .env 파일은 .gitignore에 반드시 추가Docker Compose에서는 env_file을 통해 .env 파일에서 값을 읽어오는 방식이 깔끔하다. 이때 .env 파일이 Git에 커밋되지 않도록 .gitignore에 반드시 추가해야 한다. Docker 이미지 최적화와 함께 고려하면 좋다. Kubernetes 환경이라면 Secret 리소스를 사용하여 환경변수로 주입하는 방식이 표준적이다.
Jasypt 설정 암호화, 이것이 궁금하다 — FAQ
Jasypt 암호화 키(password)는 어디에 보관해야 하나요?
절대로 소스 코드나 설정 파일에 직접 작성하면 안 된다. 가장 현실적인 방법은 OS 환경변수로 주입하는 것이다. CI/CD 파이프라인에서는 Jenkins Credentials, GitHub Actions Secrets, GitLab CI Variables 같은 시크릿 관리 기능을 활용한다. 보안 요구사항이 높은 환경이라면 HashiCorp Vault, AWS Secrets Manager 같은 전용 시크릿 관리 서비스를 검토할 수 있다.
Spring Boot 3에서 Jasypt를 사용할 수 있나요?
사용할 수 있다. jasypt-spring-boot 3.0.5부터 Spring Boot 3을 지원하기 시작했고, 4.0.4가 Spring Boot 3.5 이상을 공식 지원하는 최신 안정 버전이다. Spring Boot 3.0~3.1 범위를 사용한다면 3.0.5를, Spring Boot 3.5 이상이라면 4.0.4를 사용하는 것이 안정적이다. 기존 3.0.4 이하 버전에서 Spring Boot 3을 사용하면 Auto Configuration 로딩 실패로 인해 복호화가 동작하지 않을 수 있다.
Jasypt ENC() 복호화가 안 될 때 해결 방법은?
복호화 실패 시 가장 먼저 확인할 세 가지가 있다. 첫째, password가 정확히 전달되고 있는지 확인한다. 둘째, 암호화할 때 사용한 알고리즘과 jasypt.encryptor.algorithm 설정이 일치하는지 확인한다. 셋째, ENC() 안의 암호문이 정확한지 확인한다 — 복사 과정에서 공백이나 줄바꿈이 포함되면 복호화에 실패한다. 이 세 가지를 모두 확인해도 해결되지 않는다면, 같은 password와 알고리즘으로 새로 암호화한 값으로 교체해 보는 것이 가장 확실하다.
Jasypt와 Spring Vault의 차이점은?
Jasypt는 설정 파일에 있는 값을 암호화/복호화하는 라이브러리다. 암호화된 값 자체가 설정 파일에 남아 있고, 애플리케이션 기동 시 복호화되는 구조다. 반면 Spring Vault는 HashiCorp Vault 서버와 연동하여 시크릿을 런타임에 동적으로 가져오는 방식이다. 소규모 프로젝트나 별도 인프라 없이 빠르게 적용해야 하는 상황에서는 Jasypt가 적합하다. 시크릿 로테이션, 접근 감사 로그, 동적 자격 증명 발급 같은 엔터프라이즈 수준의 시크릿 관리가 필요하다면 Vault를 도입하는 것이 맞다.
application.yml에 jasypt password를 직접 쓰면 안 되나요?
기술적으로는 동작한다. 하지만 자물쇠와 열쇠를 같은 서랍에 넣어두는 것과 다를 바 없다. 설정 파일의 DB 비밀번호를 암호화해 놓고, 그 암호를 풀 수 있는 키를 같은 파일에 적어두면 공격자 입장에서는 한 단계가 추가됐을 뿐 아무런 보안 효과가 없다. 프로덕션 환경에서는 반드시 환경변수나 시크릿 관리 서비스를 통해 전달해야 한다.
몇 년간 Jasypt를 써오면서 느낀 것들
처음 Jasypt를 도입한 건 꽤 오래전이다. 당시에는 application.yml에 DB 비밀번호를 평문으로 넣고 사내 GitLab에 커밋하는 게 당연한 분위기였다. “어차피 사내 저장소인데 뭐”라는 생각이었는데, 어느 날 코드 리뷰에서 시니어 개발자가 “이 비밀번호, 퇴사자도 볼 수 있는 거 알지?”라고 한마디 던졌다. 그날 바로 Jasypt를 검토하기 시작했다.
초기에는 jasypt password도 application.yml에 같이 넣어뒀다. 지금 생각하면 웃기지만, 당시에는 “암호화된 값이니까 괜찮지 않나?”라는 안일한 판단이었다. 이것도 코드 리뷰에서 바로 지적당했다. 그 후로 환경변수 방식으로 전환했는데, 처음에는 dev, staging, prod 환경마다 서로 다른 password를 관리하는 게 번거롭게 느껴졌다. 하지만 CI/CD 파이프라인에 시크릿으로 등록해 놓으니 오히려 수동으로 관리할 때보다 훨씬 깔끔해졌다.
운영하면서 가장 자주 겪은 사고는 환경별 password 불일치였다. staging에서 암호화한 값을 prod에 그대로 올렸다가 기동 실패하는 경우가 몇 번 있었다. 그래서 나중에는 환경별로 암호화된 설정 파일을 분리하고, 배포 스크립트에서 환경 확인 단계를 추가했다.
Jasypt가 완벽한 솔루션인가 하면 솔직히 그렇지는 않다. 암호화 키 자체의 관리 문제는 여전히 남아 있고, 시크릿 로테이션이나 접근 감사 같은 기능은 제공하지 않는다. 규모가 커지면 결국 Vault 같은 전용 솔루션을 도입하게 된다. 하지만 “설정 파일에 평문 비밀번호가 돌아다니는 상황”에서 “최소한의 암호화라도 적용된 상황”으로 넘어가는 첫 단계로는 비용 대비 효과가 확실하다. 의존성 하나 추가하고, 환경변수 하나 설정하면 끝이라는 진입 장벽의 낮음이 Jasypt의 가장 큰 장점이라고 생각한다.
더 깊이 들어가고 싶다면 — 참고 자료
Jasypt 공식 자료
- Jasypt 공식 사이트 — Jasypt 라이브러리의 핵심 개념과 암호화 방식 설명
- jasypt-spring-boot GitHub — Spring Boot 통합 모듈의 전체 문서, 설정 옵션, 릴리스 노트
- Maven Central — jasypt-spring-boot-starter — 최신 버전 확인 및 의존성 좌표
Spring Boot 설정 관련
- Spring Boot 공식 문서 — Externalized Configuration — 프로퍼티 우선순위, 프로파일별 설정, 환경변수 바인딩 규칙
- Spring Boot 4.0 마이그레이션 가이드 — Spring Boot 3.x에서 4.0으로 안전하게 올리는 법
- Spring Security 6 JWT 인증 — Spring Boot 3 보안 설정 가이드
보안 및 시크릿 관리
- OWASP Secrets Management Cheat Sheet — 시크릿 보관, 전달, 로테이션에 대한 보안 모범 사례
- HashiCorp Vault Documentation — Jasypt 이후 단계로 고려할 수 있는 엔터프라이즈 시크릿 관리 솔루션
함께 읽으면 좋은 글
- Spring Boot 4 신기능 총정리: API 버전 관리부터 Jackson 3 마이그레이션까지
- Spring Boot Logback 설정 완벽 가이드 — logback-spring.xml 작성부터 JSON 로깅까지
- 컨테이너 환경에서 Spring Boot 콜드 스타트 6가지 개선 전략
- Spring Boot Docker 최적화 가이드: 520MB 이미지를 145MB로 줄이기까지
- Spring Security 6 (Spring Boot 3)에서 Spring Security JWT 인증