2.5 암호키 관리
개요
데이터 암호화는 정보 보안의 핵심 요소이지만, 암호화 알고리즘 자체만큼이나 암호키를 안전하게 관리하는 것이 중요합니다. 암호키가 유출되거나 부적절하게 관리되면, 아무리 강력한 암호화 알고리즘을 사용하더라도 데이터는 쉽게 복호화될 수 있습니다.
안전한 암호키 관리는 키 생명주기(Key Lifecycle) 전반에 걸쳐 이루어져야 합니다. 여기에는 안전한 키 생성, 배포, 저장, 사용, 백업/복구, 교체(갱신), 파기 등의 단계가 포함됩니다. 특히, 키를 소스코드나 설정 파일에 하드코딩하거나 평문으로 저장하는 것은 매우 위험한 행위입니다.
이 기준은 암호키의 기밀성과 무결성을 보장하고, 키 유출 및 오용으로 인한 데이터 노출 위험을 최소화하기 위한 필수적인 암호키 관리 보안 요구사항을 정의합니다.
보안 대책
- - 안전한 키 생성: 암호학적으로 안전한 난수 생성기(CSPRNG: Cryptographically Secure Pseudo-Random Number Generator)를 사용하여 예측 불가능한 키를 생성합니다. 충분한 키 길이를 사용합니다. (참조: 2.6 암호연산, 관련 약점: 충분하지 않은 키 길이 사용)
- - 안전한 키 저장:
- 하드코딩 금지: 절대로 소스코드, 설정 파일, 스크립트 등에 암호키를 직접 포함시키지 않습니다.
- 암호화 저장: 암호키 자체를 저장해야 하는 경우, 키 암호화 키(KEK: Key Encrypting Key) 또는 마스터 키를 사용하여 암호화하여 저장합니다. 키 암호화 키는 하드웨어 보안 모듈(HSM: Hardware Security Module), 안전한 키 관리 시스템(KMS: Key Management System) 또는 운영체제가 제공하는 보안 저장소 등을 이용하여 안전하게 보호합니다.
- 접근 통제: 암호키 저장소에 대한 접근 권한을 최소화하고, 접근 로그를 기록 및 감사합니다.
- - 안전한 키 배포: 암호키를 시스템 간에 전송해야 하는 경우, 반드시 보안 채널(예: TLS/SSL)을 사용하거나 키 암호화 키로 암호화하여 전송합니다.
- - 키 사용 제한 및 분리: 키의 용도(예: 암호화용, 서명용)를 명확히 하고, 용도에 맞게 키를 분리하여 사용합니다. 키 사용 횟수나 유효 기간을 제한할 수 있습니다.
- - 주기적인 키 교체(갱신): 암호키의 유효 기간을 설정하고, 주기적으로 새로운 키로 교체하여 키 유출 시 피해 범위를 제한합니다.
- - 안전한 키 파기: 사용이 만료되거나 폐기된 암호키는 복구 불가능하도록 안전하게 삭제(파기)합니다.
- - 키 관리 시스템(KMS) 또는 HSM 사용 고려: 키 생성부터 파기까지 전 생명주기를 안전하고 효율적으로 관리하기 위해 전문적인 키 관리 솔루션 도입을 고려합니다.
코드 예시 (Java - 하드코딩된 키)
암호키를 소스코드에 직접 작성하는 것은 매우 위험합니다.
취약한 코드 (하드코딩된 암호키):
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class BadEncryption {
// 암호키를 소스코드에 직접 저장 (매우 위험!)
private static final String SECRET_KEY = "ThisIsAVeryBadSecretKey123";
public static String encrypt(String strToEncrypt) {
try {
SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
// ... 복호화 메서드 ...
}
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
// 외부 설정 파일 로더 또는 키 관리 시스템 SDK import 필요
public class GoodEncryption {
// 외부 설정 파일, 환경 변수, 키 관리 시스템(KMS) 등 안전한 위치에서 키를 로드
private static final String SECRET_KEY = loadSecretKeyFromSecureSource();
public static String encrypt(String strToEncrypt) {
try {
// 로드된 키 사용
SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // ECB 대신 GCM 등 안전한 모드 사용 권장
// GCM 사용 시 IV(Initialization Vector) 관리도 중요
// cipher.init(...)
// ... 암호화 로직 ...
} catch (Exception e) {
// ... 오류 처리 ...
}
return null;
}
private static String loadSecretKeyFromSecureSource() {
// 실제 구현:
// 1. 설정 파일 (파일 접근 권한 엄격히 제어)
// 2. 환경 변수 (컨테이너 환경 등에서 사용)
// 3. Vault, AWS KMS, Azure Key Vault 등 전문 키 관리 시스템 연동
// 예시: return System.getenv("ENCRYPTION_KEY");
return "LoadedSecretKeyFromKMS"; // 실제 구현 필요
}
// ... 복호화 메서드 ...
}
Python(환경 변수, python-dotenv, Vault 라이브러리), JavaScript(Node.js의 `process.env`, KMS SDK), C#(Azure Key Vault, `ConfigurationManager`), PHP 등 모든 언어에서 암호키는 코드와 분리하여 안전하게 관리해야 합니다.