a1a5a34874
- HsmCryptoService: getPublicKey/getSecretKey ConcurrentHashMap 캐시 추가 (HSM 통신 최소화) - HsmCryptoService: encryptAes/decryptAes Provider 파라미터 오버로드 추가 (non-extractable 키 지원) - HsmCryptoService: PropertyChangeListener 구현, HSM.CACHE_RELOAD_YN=Y 시 clearKeyCache() 자동 호출 - HsmCryptoService: @PostConstruct NPE 수정 - PropManager.getInstance() → @Autowired 직접 주입 - HsmManager: DB PKCS11_CONFIG 리터럴 \n → 실제 줄바꿈 치환 (파싱 오류 수정) - HsmManager: 기동 시 HSM 키 목록 로그 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
184 lines
6.3 KiB
Java
184 lines
6.3 KiB
Java
package com.eactive.eai.common.hsm;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.File;
|
|
import java.io.InputStream;
|
|
import java.lang.reflect.Method;
|
|
import java.nio.file.Files;
|
|
import java.security.KeyStore;
|
|
import java.security.Provider;
|
|
import java.security.Security;
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import com.eactive.eai.common.exception.ExceptionUtil;
|
|
import com.eactive.eai.common.lifecycle.Lifecycle;
|
|
import com.eactive.eai.common.lifecycle.LifecycleException;
|
|
import com.eactive.eai.common.lifecycle.LifecycleListener;
|
|
import com.eactive.eai.common.lifecycle.LifecycleSupport;
|
|
import com.eactive.eai.common.property.PropManager;
|
|
import com.eactive.eai.common.util.ApplicationContextProvider;
|
|
import com.eactive.eai.common.util.Logger;
|
|
|
|
/**
|
|
* SafeNet ProtectServer HSM 연동 관리자 (JDK 8 / SunPKCS11)
|
|
*
|
|
* PropManager 그룹 "HSM" 에서 읽는 키:
|
|
* PKCS11_CONFIG - pkcs11.cfg 파일 내용 (name/library/slot 설정을 DB에 직접 저장)
|
|
* PIN - HSM 슬롯 PIN
|
|
*
|
|
* PKCS11_CONFIG 값 예시 (개행은 \n 으로 입력):
|
|
* name = ProtectServer
|
|
* library = /opt/safenet/protecttoolkit5/ptk/lib/libcryptoki.so
|
|
* slot = 0
|
|
*/
|
|
@Component
|
|
public class HsmManager implements Lifecycle {
|
|
|
|
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
|
|
|
private static final String GROUP_NAME = "HSM";
|
|
private static final String PROP_CONFIG = "PKCS11_CONFIG";
|
|
private static final String PROP_PIN = "PIN";
|
|
|
|
private Provider pkcs11Provider;
|
|
private KeyStore keyStore;
|
|
private boolean started;
|
|
|
|
private final LifecycleSupport lifecycle = new LifecycleSupport(this);
|
|
|
|
private HsmManager() {
|
|
}
|
|
|
|
public static HsmManager getInstance() {
|
|
return ApplicationContextProvider.getContext().getBean(HsmManager.class);
|
|
}
|
|
|
|
@Override
|
|
public void start() throws LifecycleException {
|
|
if (started) {
|
|
throw new LifecycleException("RECEAIHSM001");
|
|
}
|
|
lifecycle.fireLifecycleEvent(STARTING_EVENT, this);
|
|
|
|
try {
|
|
init();
|
|
} catch (Exception e) {
|
|
throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAIHSM002"));
|
|
}
|
|
|
|
started = true;
|
|
lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
|
|
}
|
|
|
|
private void init() throws Exception {
|
|
String configContent = PropManager.getInstance().getProperty(GROUP_NAME, PROP_CONFIG);
|
|
String pin = PropManager.getInstance().getProperty(GROUP_NAME, PROP_PIN);
|
|
|
|
if (configContent == null || configContent.trim().isEmpty()) {
|
|
logger.warn("HsmManager] PKCS11_CONFIG 가 설정되지 않았습니다. HSM 초기화를 건너뜁니다.");
|
|
return;
|
|
}
|
|
|
|
pkcs11Provider = createProvider(configContent.trim().replace("\\n", "\n"));
|
|
|
|
// 이미 등록된 Provider 가 있으면 제거 후 재등록
|
|
Provider existing = Security.getProvider(pkcs11Provider.getName());
|
|
if (existing != null) {
|
|
Security.removeProvider(existing.getName());
|
|
}
|
|
Security.addProvider(pkcs11Provider);
|
|
|
|
keyStore = KeyStore.getInstance("PKCS11", pkcs11Provider);
|
|
char[] pinChars = (pin != null) ? pin.toCharArray() : null;
|
|
keyStore.load(null, pinChars);
|
|
|
|
logger.warn("HsmManager] 초기화 완료. Provider=" + pkcs11Provider.getName());
|
|
|
|
java.util.Enumeration<String> aliases = keyStore.aliases();
|
|
StringBuilder aliasList = new StringBuilder();
|
|
while (aliases.hasMoreElements()) {
|
|
if (aliasList.length() > 0) aliasList.append(", ");
|
|
aliasList.append(aliases.nextElement());
|
|
}
|
|
logger.warn("HsmManager] HSM 키 목록: [" + aliasList + "]");
|
|
}
|
|
|
|
/**
|
|
* JDK 버전에 따라 SunPKCS11 Provider 를 생성한다.
|
|
*
|
|
* JDK 8 : SunPKCS11(InputStream) 생성자를 리플렉션으로 호출
|
|
* JDK 9+: Provider.configure(configFilePath) 를 리플렉션으로 호출
|
|
*
|
|
* 두 경로 모두 리플렉션을 사용하므로 컴파일 타임에 JDK 버전 의존성이 없다.
|
|
*/
|
|
static Provider createProvider(String cfgContent) throws Exception {
|
|
String javaVersion = System.getProperty("java.version");
|
|
if (javaVersion.startsWith("1.")) {
|
|
// JDK 8: new SunPKCS11(InputStream)
|
|
InputStream is = new ByteArrayInputStream(cfgContent.getBytes("UTF-8"));
|
|
return (Provider) Class.forName("sun.security.pkcs11.SunPKCS11")
|
|
.getConstructor(InputStream.class)
|
|
.newInstance(is);
|
|
} else {
|
|
// JDK 9+: Security.getProvider("SunPKCS11").configure(configFilePath)
|
|
// configure() 는 JDK 9 에서 추가된 메서드이므로 리플렉션으로 호출
|
|
File tmp = File.createTempFile("pkcs11-hsm-", ".cfg");
|
|
tmp.deleteOnExit();
|
|
Files.write(tmp.toPath(), cfgContent.getBytes("UTF-8"));
|
|
Provider base = Security.getProvider("SunPKCS11");
|
|
Method configure = Provider.class.getMethod("configure", String.class);
|
|
return (Provider) configure.invoke(base, tmp.getAbsolutePath());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void stop() throws LifecycleException {
|
|
if (!started) {
|
|
throw new LifecycleException("RECEAIHSM003");
|
|
}
|
|
lifecycle.fireLifecycleEvent(STOPING_EVENT, this);
|
|
|
|
if (pkcs11Provider != null) {
|
|
Security.removeProvider(pkcs11Provider.getName());
|
|
}
|
|
pkcs11Provider = null;
|
|
keyStore = null;
|
|
started = false;
|
|
|
|
lifecycle.fireLifecycleEvent(STOPPED_EVENT, this);
|
|
}
|
|
|
|
@Override
|
|
public void addLifecycleListener(LifecycleListener listener) {
|
|
lifecycle.addLifecycleListener(listener);
|
|
}
|
|
|
|
@Override
|
|
public LifecycleListener[] findLifecycleListeners() {
|
|
return lifecycle.findLifecycleListeners();
|
|
}
|
|
|
|
@Override
|
|
public void removeLifecycleListener(LifecycleListener listener) {
|
|
lifecycle.removeLifecycleListener(listener);
|
|
}
|
|
|
|
@Override
|
|
public boolean isStarted() {
|
|
return started;
|
|
}
|
|
|
|
public Provider getPkcs11Provider() {
|
|
return pkcs11Provider;
|
|
}
|
|
|
|
public KeyStore getKeyStore() {
|
|
return keyStore;
|
|
}
|
|
|
|
public boolean isReady() {
|
|
return started && pkcs11Provider != null && keyStore != null;
|
|
}
|
|
}
|