e208a2d64b
- 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>
159 lines
5.7 KiB
Java
159 lines
5.7 KiB
Java
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();
|
|
}
|
|
}
|