https://goddaehee.tistory.com/321
https://gksdudrb922.tistory.com/147
https://derveljunit.tistory.com/339
- Gradle
==========================================================================
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.4'
==========================================================================
- Maven
==========================================================================
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.1</version>
==========================================================================
- Gradle
==========================================================================
==========================================================================
- Maven
==========================================================================
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.13.0</version>
</dependency>
==========================================================================
별도의 프로젝트를 생성하여 데이터들을 미리 암호화 진행
※ yml파일에 암호화할 계정 정보를 입력
ex)
databaseId : kimsungchul
databasePassowrd : kimsungchulPassword123
==========================================================================
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import java.io.File;
import java.util.Map;
public class Main {
static PooledPBEStringEncryptor encryptor;
static SimpleStringPBEConfig simpleStringPBEConfig;
public static void main(String[] args){
initConfig();
String filePath = "local.yml";
ClassLoader classLoader = Main.class.getClassLoader();
File file = new File(classLoader.getResource(filePath).getFile());
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
try{
Map<String,Object> yamlMap = objectMapper.readValue(file,Map.class);
for(Map.Entry<String ,Object> entry : yamlMap.entrySet()){
encrypt(entry.getKey() , entry.getValue().toString());
//System.out.println(entry.getKey() + " : " + entry.getValue().toString());
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void initConfig(){
String key = "kim-sung-chul-password-encrypt-key";
encryptor = new PooledPBEStringEncryptor();
simpleStringPBEConfig = new SimpleStringPBEConfig();
simpleStringPBEConfig.setPassword(key); // 암호화키
simpleStringPBEConfig.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); // 알고리즘
simpleStringPBEConfig.setKeyObtentionIterations("1000"); // 반복할 해싱 회수
simpleStringPBEConfig.setPoolSize("1"); // 인스턴스 pool
simpleStringPBEConfig.setProviderName("SunJCE");
simpleStringPBEConfig.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // salt 생성 클래스
simpleStringPBEConfig.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
simpleStringPBEConfig.setStringOutputType("base64"); //인코딩 방식
encryptor.setConfig(simpleStringPBEConfig);
}
public static String encrypt(String fieldName , String word){
String encryptWord = encryptor.encrypt(word);
System.out.println("### encryptWord "+fieldName +" : " + encryptWord);
return encryptWord;
}
public static String decrypt(String fieldName , String word){
String decryptWord= encryptor.decrypt(word);
System.out.println("### decryptWord "+fieldName +" : " + decryptWord);
return decryptWord;
}
}
==========================================================================
- jasyptEncryptorAES.java
==========================================================================
@Configuration
@EnableEncryptableProperties
public class JasyptConfigAES {
@Bean("jasyptEncryptorAES")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword("kim-sung-chul-password-encrypt-key"); // 암호화키
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); // 알고리즘
config.setKeyObtentionIterations("1000"); // 반복할 해싱 회수
config.setPoolSize("1"); // 인스턴스 pool
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // salt 생성 클래스
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64"); //인코딩 방식
encryptor.setConfig(config);
return encryptor;
}
}
==========================================================================
yml 파일 아래에
==========================================================================
jasypt:
encryptor:
bean: jasyptStringEncryptor // 빈으로 등록한 빈 이름과 매핑
==========================================================================
암호화 데이터 변경시 ENC() 로 감싼 후 입력해야 함
ex )
ENC(암호화된 데이터)
- 기존
==========================================================================
spring:
datasource:
url: jdbc:mysql://...
username: user123
password: pass123
driver-class-name: com.mysql.cj.jdbc.Driver
==========================================================================
- 암호화 적용 후
==========================================================================
spring:
datasource:
url: jdbc:mysql://...
username: ENC(QK4EKadvkeEQJfrWEKAREq==)
password: ENC(QD5EZJeFSJYWIDmi75dmYQ==)
==========================================================================
솔트(Salt)와 초기화 벡터(Initialization Vector, IV)는 암호화 과정에서 사용되며, 그 목적은 보안을 강화하고 암호화된 데이터의 예측 불가능성을 높이기 위해서입니다.
솔트(Salt):
솔트는 주로 비밀번호나 다른 중요한 데이터를 해시할 때 사용됩니다.
해시 함수는 동일한 입력에 대해 항상 동일한 출력을 생성하는 단방향함수입니다.
이러한 특성 때문에, 레인보우 테이블 같은 해시값을 미리 계산해둔 테이블을 이용하여 원본 데이터를 찾아내는 공격이 가능합니다.
솔트는 이러한 공격을 방지하기 위해 사용되는 임의의 데이터로, 사용자의 비밀번호에 임의의 문자열을 추가하여 해시 함수의 입력값을 변경합니다.
예를 들어, 사용자의 비밀번호가 'mypassword'라면, 솔트 'abc123'을 추가하여 'mypasswordabc123'과 같이 만든 후 이 값을 해시하게 됩니다.
각 사용자마다 고유의 솔트를 사용함으로써, 같은 비밀번호라도 서로 다른 해시 값을 생성하여 보안을 강화합니다.
솔트는 비밀 정보가 아니기 때문에, 해시와 같이 데이터베이스에 저장할 수 있습니다.
초기화 벡터(IV):
초기화 벡터(IV)는 블록 암호화 방식에서 사용되는 개념으로, 암호화할 때 마다 다른 랜덤한 비트열을 첫 번째 블록의 암호화에 사용하는 것을 말합니다.
IV는 블록 암호모드 중에 CBC(Cipher Block Chaining) 모드 같은 몇몇 모드에서 필수적으로 요구됩니다.
블록 암호는 데이터를 일정한 크기의 블록으로 나누어 암호화합니다.
만약 동일한 데이터 블록을 동일한 키로 여러 번 암호화하면, 같은 암호문이 생성되는데, 이는 패턴을 드러내고 정보를 유추할 수 있는 통로를 제공합니다.
IV를 사용함으로써, 같은 데이터라도 매번 다른 암호문이 생성되어 이러한 패턴을 제거할 수 있습니다.
IV는 반드시 예측 불가능해야 하며, 공격자에게 알려져서는 안 됩니다.
일반적으로는 암호문과 함께 저장하거나 전송되는데, 이는 암호를 해독할 때 필요하기 때문입니다. I
V가 공격자에게 알려지더라도, 암호문 자체는 안전합니다만, IV를 반복해서 사용하거나 예측 가능하게 만드는 실수는 안전성을 크게 저하시킬 수 있습니다.
솔트와 IV 모두 중요한 보안 개념이며, 각각 해시 함수와 블록 암호에서 데이터의 안전을 보장하기 위해 중요한 역할을 합니다.
- 질문
근데 여기서 아래의 두개는 매번 값이 다르게 들어가는데, 복호화 할때문제가 없어?
simpleStringPBEConfig.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
simpleStringPBEConfig.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
- 답변
암호화 과정에서 매번 다른 솔트(Salt)와 초기화 벡터(Initialization Vector, IV)를 생성하더라도 복호화하는 과정에서 문제가 되지 않습니다.
이유는 암호화할 때 생성된 솔트와 IV가 암호화된 데이터와 함께 저장되기 때문입니다. Jasypt는 이를 자동으로 처리해주며, 복호화 시에는 이 정보를 사용해서 일관된 복호화 결과를 얻을 수 있습니다.
솔트와 IV를 도입하는 목적은 암호화 과정에서 보안을 강화하는 것입니다.
솔트는 삽입될 때마다 암호화할 내용이 동일해도 결과가 매번 달라지게 하여 패턴 분석과 레인보우 테이블 공격을 어렵게 만듭니다.
IV는 블록 암호 모드에서 첫 블록 암호화 시에 사용되며, 난수성을 보장하여 동일한 텍스트 블록의 암호화 결과가 중복되지 않도록 합니다.
복호화 시에는 암호화된 데이터와 함께 저장된 솔트와 IV 값을 사용하여 원래의 데이터를 정확하게 복구할 수 있습니다.
암호화와 복호화에 동일한 키를 사용하고, 알고리즘과 솔트, IV 값이 일치하면 알고리즘은 정상 작동하여 올바른 데이터를 복원합니다.
위 코드에서는 PooledPBEStringEncryptor와 SimpleStringPBEConfig를 활용하여 Jasypt 라이브러리를 통해 암호화 및 복호화를 수행하고 있습니다.
encryptor.encrypt 메소드는 암호화를 수행하고, encryptor.decrypt 메소드는 복호화를 수행합니다.
설정된 솔트와 IV 생성기 클래스로 인해 매번 다른 솔트와 IV가 사용되지만, 암호화된 데이터와 함께 필요한 모든 정보가 올바르게 저장 및 관리되기 때문에 복호화할 때 문제가 되지 않습니다.
다만, 코드 상에 보이는 암호키(kim-sung-chul-password-encrypt-key)는 예시로 작성된 것이며 실제 운영 환경에서는 보안을 위해 안전한 방법으로 관리되어야 합니다.