From f4c28e8a272711bbeea54c347c94727c2f6c395e Mon Sep 17 00:00:00 2001 From: curry772 Date: Tue, 12 May 2026 16:30:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20HSM=20=ED=82=A4=20=ED=8C=8C=EC=83=9D=20?= =?UTF-8?q?=EC=A0=84=EB=9E=B5=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(common=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BaseHsmKeyDerivationStrategy: HSM 마스터키 조회 공통 로직 추상 기반 클래스 - HsmKeyDerivationStrategy: HSM 마스터키 직접 사용 전략 - HsmSha256KeyDerivationStrategy: HSM 마스터키 SHA-256 해싱 도출 전략 - HsmContextSha256KeyDerivationStrategy: HSM 마스터키 + 컨텍스트 SHA-256 해싱 도출 전략 Co-Authored-By: Claude Sonnet 4.6 --- .../BaseHsmKeyDerivationStrategy.java | 40 ++++++++++++ ...HsmContextSha256KeyDerivationStrategy.java | 65 +++++++++++++++++++ .../strategy/HsmKeyDerivationStrategy.java | 24 +++++++ .../HsmSha256KeyDerivationStrategy.java | 30 +++++++++ 4 files changed, 159 insertions(+) create mode 100644 src/main/java/com/eactive/eai/common/security/keyderiv/strategy/BaseHsmKeyDerivationStrategy.java create mode 100644 src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmContextSha256KeyDerivationStrategy.java create mode 100644 src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmKeyDerivationStrategy.java create mode 100644 src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmSha256KeyDerivationStrategy.java diff --git a/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/BaseHsmKeyDerivationStrategy.java b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/BaseHsmKeyDerivationStrategy.java new file mode 100644 index 0000000..e0d9c58 --- /dev/null +++ b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/BaseHsmKeyDerivationStrategy.java @@ -0,0 +1,40 @@ +package com.eactive.eai.common.security.keyderiv.strategy; + +import java.util.Map; + +import javax.crypto.SecretKey; + +import com.eactive.eai.common.hsm.HsmCryptoService; +import com.eactive.eai.common.hsm.HsmException; +import com.eactive.eai.common.security.keyderiv.KeyDerivationStrategy; +import com.eactive.eai.common.util.ApplicationContextProvider; + +/** + * HSM 마스터키를 이용하는 전략 클래스들의 기본 클래스 + */ +public abstract class BaseHsmKeyDerivationStrategy implements KeyDerivationStrategy { + + protected static final String PARAM_HSM_KEY_ALIAS = "hsmKeyAlias"; + + protected byte[] getHsmKey(Map params) throws HsmException { + String hsmKeyAlias = required(params, PARAM_HSM_KEY_ALIAS); + return getHsmKey(hsmKeyAlias); + } + + protected byte[] getHsmKey(String hsmKeyAlias) throws HsmException { + SecretKey masterKey = hsmCryptoService().getSecretKey(hsmKeyAlias); + return masterKey.getEncoded(); + } + + private String required(Map params, String key) { + String value = params.get(key); + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("key_deriv_params 필수값 누락: " + key); + } + return value; + } + + private HsmCryptoService hsmCryptoService() { + return ApplicationContextProvider.getContext().getBean(HsmCryptoService.class); + } +} diff --git a/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmContextSha256KeyDerivationStrategy.java b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmContextSha256KeyDerivationStrategy.java new file mode 100644 index 0000000..4fe6a39 --- /dev/null +++ b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmContextSha256KeyDerivationStrategy.java @@ -0,0 +1,65 @@ +package com.eactive.eai.common.security.keyderiv.strategy; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Map; + +import javax.crypto.SecretKey; + +import com.eactive.eai.common.hsm.HsmCryptoService; +import com.eactive.eai.common.security.keyderiv.DerivedKey; +import com.eactive.eai.common.security.keyderiv.KeyDerivationStrategy; +import com.eactive.eai.common.util.ApplicationContextProvider; + +/** + * HSM 마스터키 + 런타임 컨텍스트값을 연결(concatenate)한 뒤 SHA-256 해싱으로 키를 도출하는 전략. + * SHA-256 출력은 항상 32바이트(AES-256)이므로 별도 keyLength 파라미터가 불필요하다. + * + * key_deriv_params (JSON) 예시: + * { + * "hsmKeyAlias" : "MASTER_KEY_AES", + * "contextKey" : "X-Api-Group-Seq" -- runtimeContext에서 꺼낼 키 이름 + * } + */ +public class HsmContextSha256KeyDerivationStrategy implements KeyDerivationStrategy { + + private static final String PARAM_HSM_KEY_ALIAS = "hsmKeyAlias"; + private static final String PARAM_CONTEXT_KEY = "contextKey"; + + @Override + public DerivedKey deriveKey(Map params, Map runtimeContext) throws Exception { + String hsmKeyAlias = required(params, PARAM_HSM_KEY_ALIAS); + String contextKey = required(params, PARAM_CONTEXT_KEY); + + SecretKey masterKey = hsmCryptoService().getSecretKey(hsmKeyAlias); + byte[] masterKeyBytes = masterKey.getEncoded(); + byte[] contextBytes = runtimeContext.getOrDefault(contextKey, "") + .getBytes(StandardCharsets.UTF_8); + + byte[] combined = new byte[masterKeyBytes.length + contextBytes.length]; + System.arraycopy(masterKeyBytes, 0, combined, 0, masterKeyBytes.length); + System.arraycopy(contextBytes, 0, combined, masterKeyBytes.length, contextBytes.length); + + byte[] derived = MessageDigest.getInstance("SHA-256").digest(combined); + return new DerivedKey(derived, derived); + } + + @Override + public String buildCacheKey(String cryptoName, Map params, Map runtimeContext) { + String contextKey = params.getOrDefault(PARAM_CONTEXT_KEY, ""); + String contextValue = runtimeContext.getOrDefault(contextKey, ""); + return cryptoName + ":" + contextValue; + } + + private String required(Map params, String key) { + String value = params.get(key); + if (value == null || value.trim().isEmpty()) { + throw new IllegalArgumentException("key_deriv_params 필수값 누락: " + key); + } + return value; + } + + private HsmCryptoService hsmCryptoService() { + return ApplicationContextProvider.getContext().getBean(HsmCryptoService.class); + } +} diff --git a/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmKeyDerivationStrategy.java b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmKeyDerivationStrategy.java new file mode 100644 index 0000000..aa84439 --- /dev/null +++ b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmKeyDerivationStrategy.java @@ -0,0 +1,24 @@ +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"} + */ +public class HsmKeyDerivationStrategy extends BaseHsmKeyDerivationStrategy { + + @Override + public DerivedKey deriveKey(Map params, Map runtimeContext) throws Exception { + byte[] masterKeyBytes = super.getHsmKey(params); + return new DerivedKey(masterKeyBytes, masterKeyBytes); + } + + @Override + public String buildCacheKey(String cryptoName, Map params, Map runtimeContext) { + return cryptoName; + } +} diff --git a/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmSha256KeyDerivationStrategy.java b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmSha256KeyDerivationStrategy.java new file mode 100644 index 0000000..9fc7ac5 --- /dev/null +++ b/src/main/java/com/eactive/eai/common/security/keyderiv/strategy/HsmSha256KeyDerivationStrategy.java @@ -0,0 +1,30 @@ +package com.eactive.eai.common.security.keyderiv.strategy; + +import java.security.MessageDigest; +import java.util.Map; + +import com.eactive.eai.common.security.keyderiv.DerivedKey; + +/** + * HSM 마스터키를 SHA-256 해싱으로 키를 도출하는 전략. + * SHA-256 출력은 항상 32바이트(AES-256)이므로 별도 keyLength 파라미터가 불필요하다. + * + * key_deriv_params (JSON) 예시: + * { + * "hsmKeyAlias" : "MASTER_KEY_AES" + * } + */ +public class HsmSha256KeyDerivationStrategy extends BaseHsmKeyDerivationStrategy { + + @Override + public DerivedKey deriveKey(Map params, Map runtimeContext) throws Exception { + byte[] masterKeyBytes = super.getHsmKey(params); + byte[] derived = MessageDigest.getInstance("SHA-256").digest(masterKeyBytes); + return new DerivedKey(derived, derived); + } + + @Override + public String buildCacheKey(String cryptoName, Map params, Map runtimeContext) { + return cryptoName; + } +}