feat: CryptoFilter 프로퍼티 키 규격화 및 HSM 통합 테스트 추가
- CryptoFilter prop 키를 UPPER_UNDERSCORE 형식으로 변경 (CRYPTO_MODULE_NAME 등) - CRYPTO_DEC_ENABLED / CRYPTO_ENC_ENABLED 옵션 제거 (필터 설정 시 항상 암복호화) - getProp() 빈 문자열도 기본값으로 처리 (StringUtils.isBlank 적용) - CryptoFilterHsmIntegrationTest 추가: SoftHSM2 연동 전체 흐름 검증 (7개 케이스) - attributes(*, CKO_SECRET_KEY, CKK_AES): 임포트 키도 CKA_SENSITIVE=false 적용 - HsmKeyDerivationStrategy / HsmContextSha256KeyDerivationStrategy 검증 - HsmKeyRegisterUtil 추가: SoftHSM2 마스터키 등록 유틸리티 - CryptoModuleServiceTest: GCM AAD 관련 테스트 케이스 보강 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
package com.eactive.eai.common.hsm;
|
||||
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.Enumeration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
/**
|
||||
* SoftHSM2 마스터키 등록 유틸리티 테스트.
|
||||
*
|
||||
* 용도: CryptoModuleConfig.keyDerivParams 의 hsmKeyAlias 에 대응하는
|
||||
* AES-256 마스터키를 SoftHSM2 토큰에 등록한다.
|
||||
*
|
||||
* 사전 조건:
|
||||
* C:\SoftHSM2 에 SoftHSM2 설치
|
||||
* (토큰이 없으면 자동 초기화, 있으면 기존 토큰 재사용)
|
||||
*
|
||||
* 실행 후 확인:
|
||||
* softhsm2-util.exe --show-slots
|
||||
* 또는 pkcs11-tool --list-objects (OpenSC 설치 시)
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class HsmKeyRegisterUtil {
|
||||
|
||||
private static final String SOFTHSM2_DLL = "C:/SoftHSM2/lib/softhsm2-x64.dll";
|
||||
private static final String SOFTHSM2_UTIL = "C:/SoftHSM2/bin/softhsm2-util.exe";
|
||||
private static final String TOKEN_LABEL = "eapim-test";
|
||||
private static final String PIN = "1234";
|
||||
|
||||
/** DB CryptoModuleConfig.keyDerivParams.hsmKeyAlias 와 반드시 일치해야 한다. */
|
||||
private static final String MASTER_KEY_ALIAS = "MASTER_KEY";
|
||||
|
||||
private static Provider provider;
|
||||
private static KeyStore keyStore;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws Exception {
|
||||
System.setProperty("SOFTHSM2_CONF", "C:\\SoftHSM2\\etc\\softhsm2.conf");
|
||||
assumeTrue(new File(SOFTHSM2_DLL).exists(),
|
||||
"SoftHSM2 미설치 (C:/SoftHSM2) - 테스트 건너뜀");
|
||||
|
||||
ensureTokenInitialized();
|
||||
|
||||
String cfgContent = "name = SoftHSM\n"
|
||||
+ "library = " + SOFTHSM2_DLL + "\n"
|
||||
+ "slotListIndex = 0\n";
|
||||
|
||||
provider = HsmManager.createProvider(cfgContent);
|
||||
Provider existing = Security.getProvider(provider.getName());
|
||||
if (existing != null) Security.removeProvider(existing.getName());
|
||||
Security.addProvider(provider);
|
||||
|
||||
keyStore = KeyStore.getInstance("PKCS11", provider);
|
||||
keyStore.load(null, PIN.toCharArray());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 1. 현재 등록된 키 목록 출력
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void listKeys() throws Exception {
|
||||
System.out.println("=== HSM 키 목록 ===");
|
||||
Enumeration<String> aliases = keyStore.aliases();
|
||||
if (!aliases.hasMoreElements()) {
|
||||
System.out.println(" (등록된 키 없음)");
|
||||
}
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = aliases.nextElement();
|
||||
String type = keyStore.isKeyEntry(alias) ? "KEY" : "CERT";
|
||||
System.out.println(" [" + type + "] " + alias);
|
||||
}
|
||||
System.out.println("==================");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 2. 마스터키 등록 (없는 경우에만)
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void registerMasterKey() throws Exception {
|
||||
if (keyStore.containsAlias(MASTER_KEY_ALIAS)) {
|
||||
System.out.println("[SKIP] 이미 등록됨: " + MASTER_KEY_ALIAS);
|
||||
return;
|
||||
}
|
||||
|
||||
KeyGenerator kg = KeyGenerator.getInstance("AES", provider);
|
||||
kg.init(256);
|
||||
SecretKey sk = kg.generateKey();
|
||||
keyStore.setKeyEntry(MASTER_KEY_ALIAS, sk, null, null);
|
||||
|
||||
System.out.println("[OK] AES-256 마스터키 등록 완료: alias=" + MASTER_KEY_ALIAS);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 3. 등록 후 조회 확인
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void verifyMasterKey() throws Exception {
|
||||
keyStore.load(null, PIN.toCharArray()); // 갱신
|
||||
|
||||
assert keyStore.containsAlias(MASTER_KEY_ALIAS)
|
||||
: "마스터키 등록 실패: " + MASTER_KEY_ALIAS;
|
||||
|
||||
SecretKey sk = (SecretKey) keyStore.getKey(MASTER_KEY_ALIAS, null);
|
||||
assert sk != null : "키 조회 실패";
|
||||
assert "AES".equals(sk.getAlgorithm()) : "알고리즘 불일치: " + sk.getAlgorithm();
|
||||
|
||||
System.out.println("[OK] 마스터키 조회 성공: alias=" + MASTER_KEY_ALIAS
|
||||
+ " / algorithm=" + sk.getAlgorithm());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 헬퍼
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private static void ensureTokenInitialized() throws Exception {
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
SOFTHSM2_UTIL, "--init-token", "--free",
|
||||
"--label", TOKEN_LABEL,
|
||||
"--pin", PIN,
|
||||
"--so-pin", PIN);
|
||||
pb.redirectErrorStream(true);
|
||||
Process p = pb.start();
|
||||
String out = readOutput(p);
|
||||
p.waitFor(10, TimeUnit.SECONDS);
|
||||
System.out.println("[SoftHSM2] init-token: " + out.trim());
|
||||
}
|
||||
|
||||
private static String readOutput(Process p) throws Exception {
|
||||
InputStream is = p.getInputStream();
|
||||
byte[] buf = new byte[4096];
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int len;
|
||||
while ((len = is.read(buf)) != -1) {
|
||||
sb.append(new String(buf, 0, len, "UTF-8"));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user