Dynamic IV 도출 지원 — DerivedKey iv 필드 추가, HsmKeyAndIvDerivationStrategy 구현
This commit is contained in:
@@ -144,8 +144,12 @@ public class CryptoModuleManager implements Lifecycle {
|
||||
dynamicKeyCache.put(cacheKey, new CachedDerivedKey(derivedKey, ttl));
|
||||
}
|
||||
}
|
||||
|
||||
byte[] iv = vo.getIvHex() != null ? DatatypeConverter.parseHexBinary(vo.getIvHex()) : null;
|
||||
byte[] iv = null;
|
||||
if(derivedKey.getIv() != null) {
|
||||
iv = derivedKey.getIv();
|
||||
} else if (vo.getIvHex() != null){
|
||||
iv = DatatypeConverter.parseHexBinary(vo.getIvHex());
|
||||
}
|
||||
return buildExtension(vo, iv, derivedKey.getEncKey(), derivedKey.getDecKey());
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,19 @@ public class DerivedKey {
|
||||
|
||||
private final byte[] encKey;
|
||||
private final byte[] decKey;
|
||||
private final byte[] iv;
|
||||
|
||||
|
||||
public DerivedKey(byte[] encKey, byte[] decKey) {
|
||||
this.encKey = encKey;
|
||||
this.decKey = decKey != null ? decKey : encKey;
|
||||
this.iv = null;
|
||||
}
|
||||
|
||||
public DerivedKey(byte[] encKey, byte[] decKey, byte[] iv) {
|
||||
this.encKey = encKey;
|
||||
this.decKey = decKey != null ? decKey : encKey;
|
||||
this.iv = iv;
|
||||
}
|
||||
|
||||
public byte[] getEncKey() {
|
||||
@@ -17,4 +26,8 @@ public class DerivedKey {
|
||||
public byte[] getDecKey() {
|
||||
return decKey;
|
||||
}
|
||||
|
||||
public byte[] getIv() {
|
||||
return iv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import java.util.Map;
|
||||
public interface KeyDerivationStrategy {
|
||||
|
||||
public static final String PARAM_HSM_KEY_ALIAS = "hsmKeyAlias";
|
||||
public static final String PARAM_CONTEXT_KEY = "contextKey";
|
||||
public static final String PARAM_HSM_IV_ALIAS = "hsmIvAlias";
|
||||
public static final String PARAM_CONTEXT_KEY = "contextKey";
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+5
@@ -24,6 +24,11 @@ public abstract class BaseHsmKeyDerivationStrategy implements KeyDerivationStrat
|
||||
return masterKey.getEncoded();
|
||||
}
|
||||
|
||||
protected byte[] getHsmIv(Map<String, String> params) throws HsmException {
|
||||
String hsmKeyAlias = required(params, PARAM_HSM_IV_ALIAS);
|
||||
return getHsmKey(hsmKeyAlias);
|
||||
}
|
||||
|
||||
private String required(Map<String, String> params, String key) {
|
||||
String value = params.get(key);
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.eactive.eai.common.security.keyderiv.strategy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.eactive.eai.common.security.keyderiv.DerivedKey;
|
||||
|
||||
/**
|
||||
* HSM 마스터키를 사용하는 전략
|
||||
*
|
||||
* key_deriv_params (JSON) 예시: { "hsmKeyAlias" : "MASTER_KEY_AES", "hsmIvAlias" : "MASTER_IV_AES"}
|
||||
*/
|
||||
public class HsmKeyAndIvDerivationStrategy extends BaseHsmKeyDerivationStrategy {
|
||||
|
||||
@Override
|
||||
public DerivedKey deriveKey(Map<String, String> params, Map<String, String> runtimeContext) throws Exception {
|
||||
byte[] masterKeyBytes = super.getHsmKey(params);
|
||||
byte[] iv = super.getHsmIv(params);
|
||||
return new DerivedKey(masterKeyBytes, masterKeyBytes, iv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildCacheKey(String cryptoName, Map<String, String> params, Map<String, String> runtimeContext) {
|
||||
return cryptoName;
|
||||
}
|
||||
}
|
||||
@@ -36,4 +36,29 @@ class DerivedKeyTest {
|
||||
|
||||
assertSame(dk.getEncKey(), dk.getDecKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-4. 2-arg 생성자 — iv는 null")
|
||||
void testTwoArgConstructor_ivIsNull() {
|
||||
DerivedKey dk = new DerivedKey(new byte[]{1}, new byte[]{2});
|
||||
|
||||
assertNull(dk.getIv(), "2-arg 생성자로 생성 시 iv는 null이어야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-5. 3-arg 생성자 — iv 반환")
|
||||
void testThreeArgConstructor_ivReturned() {
|
||||
byte[] iv = {0x01, 0x02, 0x03};
|
||||
DerivedKey dk = new DerivedKey(new byte[]{1}, new byte[]{2}, iv);
|
||||
|
||||
assertArrayEquals(iv, dk.getIv());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-6. 3-arg 생성자 — iv null 허용")
|
||||
void testThreeArgConstructor_nullIv() {
|
||||
DerivedKey dk = new DerivedKey(new byte[]{1}, new byte[]{2}, null);
|
||||
|
||||
assertNull(dk.getIv());
|
||||
}
|
||||
}
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package com.eactive.eai.common.security.keyderiv;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import com.eactive.eai.common.hsm.HsmCryptoService;
|
||||
import com.eactive.eai.common.security.keyderiv.strategy.HsmKeyAndIvDerivationStrategy;
|
||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
|
||||
/**
|
||||
* HsmKeyAndIvDerivationStrategy 단위 테스트
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.DisplayName.class)
|
||||
class HsmKeyAndIvDerivationStrategyTest {
|
||||
|
||||
private static final String HSM_KEY_ALIAS = "MASTER_KEY_AES";
|
||||
private static final String HSM_IV_ALIAS = "MASTER_IV_AES";
|
||||
private static final byte[] MASTER_KEY = new byte[]{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
|
||||
0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10};
|
||||
private static final byte[] MASTER_IV = new byte[]{0x10,0x20,0x30,0x40,0x50,0x60,0x70,(byte)0x80,
|
||||
0x11,0x21,0x31,0x41,0x51,0x61,0x71,(byte)0x81};
|
||||
|
||||
private static GenericApplicationContext ctx;
|
||||
private static HsmCryptoService mockHsmCryptoService;
|
||||
private static HsmKeyAndIvDerivationStrategy strategy;
|
||||
|
||||
private Map<String, String> params;
|
||||
private Map<String, String> runtimeContext;
|
||||
|
||||
@BeforeAll
|
||||
static void setUpClass() throws Exception {
|
||||
mockHsmCryptoService = mock(HsmCryptoService.class);
|
||||
when(mockHsmCryptoService.getSecretKey(HSM_KEY_ALIAS))
|
||||
.thenReturn(new SecretKeySpec(MASTER_KEY, "AES"));
|
||||
when(mockHsmCryptoService.getSecretKey(HSM_IV_ALIAS))
|
||||
.thenReturn(new SecretKeySpec(MASTER_IV, "AES"));
|
||||
|
||||
ctx = new GenericApplicationContext();
|
||||
ctx.getBeanFactory().registerSingleton("hsmCryptoService", mockHsmCryptoService);
|
||||
ctx.registerBeanDefinition("applicationContextProvider",
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ApplicationContextProvider.class)
|
||||
.getBeanDefinition());
|
||||
ctx.refresh();
|
||||
|
||||
strategy = new HsmKeyAndIvDerivationStrategy();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUpParams() {
|
||||
params = new HashMap<>();
|
||||
params.put("hsmKeyAlias", HSM_KEY_ALIAS);
|
||||
params.put("hsmIvAlias", HSM_IV_ALIAS);
|
||||
|
||||
runtimeContext = new HashMap<>();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 1. deriveKey
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("1-1. 정상 파라미터 — DerivedKey 반환, key와 iv 모두 non-null")
|
||||
void testDeriveKey_validParams_returnsKeyAndIv() throws Exception {
|
||||
DerivedKey dk = strategy.deriveKey(params, runtimeContext);
|
||||
|
||||
assertNotNull(dk);
|
||||
assertNotNull(dk.getEncKey(), "encKey는 null이 아니어야 한다");
|
||||
assertNotNull(dk.getDecKey(), "decKey는 null이 아니어야 한다");
|
||||
assertNotNull(dk.getIv(), "iv는 null이 아니어야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-2. encKey == decKey (대칭 방식)")
|
||||
void testDeriveKey_encKeyEqualsDecKey() throws Exception {
|
||||
DerivedKey dk = strategy.deriveKey(params, runtimeContext);
|
||||
|
||||
assertArrayEquals(dk.getEncKey(), dk.getDecKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-3. encKey는 hsmKeyAlias에 해당하는 HSM 키")
|
||||
void testDeriveKey_encKeyMatchesMasterKey() throws Exception {
|
||||
DerivedKey dk = strategy.deriveKey(params, runtimeContext);
|
||||
|
||||
assertArrayEquals(MASTER_KEY, dk.getEncKey(), "encKey는 hsmKeyAlias로 가져온 마스터키여야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-4. iv는 hsmIvAlias에 해당하는 HSM 키")
|
||||
void testDeriveKey_ivMatchesMasterIv() throws Exception {
|
||||
DerivedKey dk = strategy.deriveKey(params, runtimeContext);
|
||||
|
||||
assertArrayEquals(MASTER_IV, dk.getIv(), "iv는 hsmIvAlias로 가져온 HSM 값이어야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-5. 필수 파라미터 누락(hsmKeyAlias) — IllegalArgumentException")
|
||||
void testDeriveKey_missingHsmKeyAlias_throwsException() {
|
||||
params.remove("hsmKeyAlias");
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> strategy.deriveKey(params, runtimeContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-6. 필수 파라미터 누락(hsmIvAlias) — IllegalArgumentException")
|
||||
void testDeriveKey_missingHsmIvAlias_throwsException() {
|
||||
params.remove("hsmIvAlias");
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> strategy.deriveKey(params, runtimeContext));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 2. buildCacheKey
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("2-1. buildCacheKey — cryptoName 단독 반환")
|
||||
void testBuildCacheKey_returnsCryptoName() {
|
||||
String cacheKey = strategy.buildCacheKey("CRYPTO_A", params, runtimeContext);
|
||||
|
||||
assertEquals("CRYPTO_A", cacheKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("2-2. buildCacheKey — 다른 cryptoName은 다른 캐시 키")
|
||||
void testBuildCacheKey_differentCryptoName_differentKey() {
|
||||
String key1 = strategy.buildCacheKey("CRYPTO_A", params, runtimeContext);
|
||||
String key2 = strategy.buildCacheKey("CRYPTO_B", params, runtimeContext);
|
||||
|
||||
assertNotEquals(key1, key2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user