164 lines
7.2 KiB
Markdown
164 lines
7.2 KiB
Markdown
패스워드 기반 키 생성(Password-Based Key Derivation, PBKD)은 사용자가 제공한 패스워드(일반적으로 짧고 사람이 기억하기 쉬운 문자열)를 기반으로 암호화에 사용할 수 있는 안전하고 고정된 길이의 암호화 키를 생성하는 방법입니다. 자바에서는 JCE(Java Cryptography Extension)를 통해 이를 구현할 수 있으며, 대표적으로 **PBKDF2**(Password-Based Key Derivation Function 2)가 널리 사용됩니다.
|
|
|
|
아래에서 패스워드 기반 키 생성의 개념, 동작 원리, 주요 알고리즘, 자바에서의 구현 방법 등을 상세히 설명하겠습니다.
|
|
|
|
---
|
|
|
|
### **패스워드 기반 키 생성의 개념**
|
|
- **문제점**: 패스워드는 보통 길이가 짧고 엔트로피(무작위성)가 낮아 직접 암호화 키로 사용하기에는 부적합합니다. 예를 들어, AES 키는 128비트(16바이트) 이상이어야 하지만, "password123" 같은 패스워드는 그보다 훨씬 짧고 예측 가능합니다.
|
|
- **해결책**: PBKD는 패스워드에 **솔트(Salt)**와 반복적인 해시 연산을 적용하여 안전하고 고정된 길이의 키를 생성합니다.
|
|
- **목적**:
|
|
1. 패스워드의 엔트로피를 높여 무차별 대입 공격(Brute Force)이나 사전 공격(Dictionary Attack)을 어렵게 만듦.
|
|
2. AES, RSA 등 암호화 알고리즘이 요구하는 특정 길이의 키(예: 256비트)를 생성.
|
|
|
|
---
|
|
|
|
### **주요 구성 요소**
|
|
1. **패스워드(Password)**:
|
|
- 사용자가 제공한 문자열(예: "MySecretPass123").
|
|
- 보통 유니코드 문자로 표현되며, 직접 키로 사용되지 않음.
|
|
|
|
2. **솔트(Salt)**:
|
|
- 무작위로 생성된 바이트 배열.
|
|
- 동일한 패스워드라도 다른 키를 생성하도록 보장하며, 사전 공격(Precomputed Table, Rainbow Table)을 방지.
|
|
- 일반적으로 8바이트 이상(16바이트 권장).
|
|
|
|
3. **반복 횟수(Iteration Count)**:
|
|
- 해시 함수를 반복 적용하는 횟수.
|
|
- 계산 비용을 높여 무차별 대입 공격을 느리게 만듦.
|
|
- 예: 10,000회 이상(현대 하드웨어 기준으로는 100,000회 이상 권장).
|
|
|
|
4. **키 길이(Key Length)**:
|
|
- 출력할 키의 길이(예: AES 256비트 = 32바이트).
|
|
- 사용하려는 암호화 알고리즘에 맞게 지정.
|
|
|
|
5. **해시 알고리즘**:
|
|
- 내부적으로 사용되는 암호화 해시 함수(예: SHA-256, SHA-512 등).
|
|
|
|
---
|
|
|
|
### **대표 알고리즘: PBKDF2**
|
|
- **정의**: NIST와 RSA에서 표준화된 알고리즘으로, HMAC(Hash-based Message Authentication Code)을 기반으로 키를 생성.
|
|
- **동작 방식**:
|
|
1. 패스워드와 솔트를 입력으로 HMAC 함수(예: HMAC-SHA256)에 넣음.
|
|
2. 지정된 반복 횟수만큼 HMAC 연산을 반복.
|
|
3. 결과로 고정된 길이의 키를 출력.
|
|
- **표기**: 자바에서는 `"PBKDF2WithHmacSHA256"`처럼 HMAC와 결합된 해시 알고리즘을 지정.
|
|
|
|
#### **지원되는 해시 알고리즘**
|
|
- `PBKDF2WithHmacSHA1` (구형, 권장되지 않음).
|
|
- `PBKDF2WithHmacSHA256` (권장).
|
|
- `PBKDF2WithHmacSHA512` (더 높은 보안).
|
|
|
|
---
|
|
|
|
### **자바에서 PBKDF2 구현 예제**
|
|
자바에서는 `PBEKeySpec`와 `SecretKeyFactory`를 사용하여 PBKDF2 기반 키를 생성할 수 있습니다.
|
|
|
|
```java
|
|
import javax.crypto.SecretKey;
|
|
import javax.crypto.SecretKeyFactory;
|
|
import javax.crypto.spec.PBEKeySpec;
|
|
import java.security.SecureRandom;
|
|
import java.security.spec.KeySpec;
|
|
import java.util.Base64;
|
|
|
|
public class Pbkdf2Example {
|
|
public static void main(String[] args) throws Exception {
|
|
// 1. 패스워드 정의
|
|
String password = "MySecretPass123";
|
|
|
|
// 2. 솔트 생성
|
|
SecureRandom random = new SecureRandom();
|
|
byte[] salt = new byte[16]; // 16바이트 솔트
|
|
random.nextBytes(salt);
|
|
|
|
// 3. PBKDF2 매개변수 설정
|
|
int iterationCount = 100000; // 반복 횟수
|
|
int keyLength = 256; // 출력 키 길이 (AES 256비트)
|
|
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength);
|
|
|
|
// 4. PBKDF2 키 생성
|
|
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
|
SecretKey key = factory.generateSecret(spec);
|
|
|
|
// 5. 생성된 키 출력
|
|
byte[] keyBytes = key.getEncoded();
|
|
String keyBase64 = Base64.getEncoder().encodeToString(keyBytes);
|
|
System.out.println("Generated Key: " + keyBase64);
|
|
System.out.println("Salt: " + Base64.getEncoder().encodeToString(salt));
|
|
}
|
|
}
|
|
```
|
|
|
|
#### **출력 예시**
|
|
```
|
|
Generated Key: X7k... (32바이트 Base64 인코딩)
|
|
Salt: abC... (16바이트 Base64 인코딩)
|
|
```
|
|
|
|
---
|
|
|
|
### **AES와 결합한 예제**
|
|
생성된 키를 AES-GCM 암호화에 사용하는 예제입니다.
|
|
|
|
```java
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.SecretKey;
|
|
import javax.crypto.spec.GCMParameterSpec;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.SecureRandom;
|
|
|
|
public class Pbkdf2AesGcmExample {
|
|
public static void main(String[] args) throws Exception {
|
|
// 1. PBKDF2로 키 생성 (위 예제에서 가져옴)
|
|
String password = "MySecretPass123";
|
|
byte[] salt = new byte[16];
|
|
SecureRandom random = new SecureRandom();
|
|
random.nextBytes(salt);
|
|
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100000, 256);
|
|
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
|
SecretKey pbkdf2Key = factory.generateSecret(spec);
|
|
SecretKey key = new SecretKeySpec(pbkdf2Key.getEncoded(), "AES");
|
|
|
|
// 2. AES-GCM 설정
|
|
byte[] iv = new byte[12]; // 12바이트 Nonce
|
|
random.nextBytes(iv);
|
|
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); // 128비트 태그
|
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
|
|
|
|
// 3. 암호화
|
|
byte[] plaintext = "Hello, PBKDF2!".getBytes(StandardCharsets.UTF_8);
|
|
byte[] ciphertext = cipher.doFinal(plaintext);
|
|
|
|
// 출력
|
|
System.out.println("Ciphertext: " + Base64.getEncoder().encodeToString(ciphertext));
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### **장점**
|
|
1. **보안성**: 솔트와 반복 연산으로 패스워드의 약점을 보완.
|
|
2. **유연성**: 출력 키 길이를 자유롭게 조정 가능.
|
|
3. **표준화**: PBKDF2는 NIST SP 800-132 등에서 권장.
|
|
|
|
### **단점 및 주의사항**
|
|
1. **성능**: 반복 횟수가 많을수록 계산 비용 증가 (의도된 설계이나 최적화 필요).
|
|
2. **솔트 관리**: 솔트는 암호화된 데이터와 함께 저장해야 하며, 재사용 시 동일한 키를 생성해야 함.
|
|
3. **현대 대안**: Argon2, bcrypt, scrypt 등이 더 강력한 대안으로 주목받고 있음(자바 기본 JCE에서는 미지원, 외부 라이브러리 필요).
|
|
|
|
---
|
|
|
|
### **권장 설정**
|
|
- **솔트**: 최소 16바이트, SecureRandom으로 생성.
|
|
- **반복 횟수**: 하드웨어 성능에 따라 100,000회 이상(예: 2025년 기준 1초 이내 처리 목표).
|
|
- **해시**: SHA-256 또는 SHA-512.
|
|
- **키 길이**: AES 사용 시 256비트 권장.
|
|
|
|
---
|
|
|
|
패스워드 기반 키 생성에 대해 더 궁금한 점이나 특정 부분에 대한 추가 설명이 필요하면 말씀해주세요! |