CryptoModuleManager 기반 암호화모듈 진단/암복호화 테스트 관리 API 추가
/manage/crypto 하위에 등록된 암호화모듈 목록/단건 조회, DYNAMIC 키 전략(KeyDerivationStrategy) 로드 상태 확인, 동적 키 캐시 키 목록 조회, Base64 기반 암복호화 라운드트립 테스트 엔드포인트를 제공한다. 원본 암호키(encKeyHex/decKeyHex/ivHex)는 응답에 포함하지 않는다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package com.eactive.eai.common.security;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* CryptoModuleManager 진단정보 DTO.
|
||||
*
|
||||
* 원본 암호키(encKey/decKey) 및 IV는 절대 포함하지 않는다. 메타데이터와
|
||||
* 키 도출 전략(KeyDerivationStrategy) 로드 상태만 담는다.
|
||||
*/
|
||||
@Data
|
||||
public class CryptoModuleDiagnosticDTO {
|
||||
|
||||
String cryptoName;
|
||||
String cryptoDesc;
|
||||
String algType;
|
||||
String cipherMode;
|
||||
String padding;
|
||||
boolean hasIv;
|
||||
String keySourceType;
|
||||
String keyDerivStrategy;
|
||||
boolean strategyLoaded;
|
||||
String strategyClassName;
|
||||
String strategyLoadError;
|
||||
String cacheYn;
|
||||
Integer cacheTtlSec;
|
||||
String useYn;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.eactive.eai.common.security;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -153,6 +154,58 @@ public class CryptoModuleManager implements Lifecycle {
|
||||
return buildExtension(vo, iv, derivedKey.getEncKey(), derivedKey.getDecKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록된 전체 암호화모듈의 진단정보를 반환한다.
|
||||
* 원본 키(encKey/decKey/ivHex)는 포함하지 않는다.
|
||||
*/
|
||||
public List<CryptoModuleDiagnosticDTO> describeAll() {
|
||||
List<CryptoModuleDiagnosticDTO> result = new ArrayList<>();
|
||||
for (String cryptoName : configMap.keySet()) {
|
||||
result.add(describe(cryptoName));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 암호화모듈의 진단정보를 반환한다. DYNAMIC 키 방식인 경우 resolveStrategy를 통해
|
||||
* 전략 클래스 로드 성공 여부를 함께 확인한다. 원본 키(encKey/decKey/ivHex)는 포함하지 않는다.
|
||||
*/
|
||||
public CryptoModuleDiagnosticDTO describe(String cryptoName) {
|
||||
CryptoModuleConfigVO vo = getVO(cryptoName);
|
||||
|
||||
CryptoModuleDiagnosticDTO dto = new CryptoModuleDiagnosticDTO();
|
||||
dto.setCryptoName(vo.getCryptoName());
|
||||
dto.setCryptoDesc(vo.getCryptoDesc());
|
||||
dto.setAlgType(vo.getAlgType());
|
||||
dto.setCipherMode(vo.getCipherMode());
|
||||
dto.setPadding(vo.getPadding());
|
||||
dto.setHasIv(vo.getIvHex() != null);
|
||||
dto.setKeySourceType(vo.getKeySourceType());
|
||||
dto.setCacheYn(vo.getCacheYn());
|
||||
dto.setCacheTtlSec(vo.getCacheTtlSec());
|
||||
dto.setUseYn(vo.getUseYn());
|
||||
|
||||
if (!"STATIC".equalsIgnoreCase(vo.getKeySourceType()) && vo.getKeyDerivStrategy() != null) {
|
||||
dto.setKeyDerivStrategy(vo.getKeyDerivStrategy());
|
||||
try {
|
||||
KeyDerivationStrategy strategy = resolveStrategy(vo.getKeyDerivStrategy());
|
||||
dto.setStrategyLoaded(true);
|
||||
dto.setStrategyClassName(strategy.getClass().getName());
|
||||
} catch (Exception e) {
|
||||
dto.setStrategyLoaded(false);
|
||||
dto.setStrategyLoadError(e.getMessage());
|
||||
}
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 동적 키 캐시에 존재하는 캐시 키 목록을 반환한다. (전략별 buildCacheKey 결과이며, 원본 키 값이 아니다)
|
||||
*/
|
||||
public List<String> listDynamicCacheKeys() {
|
||||
return new ArrayList<>(dynamicKeyCache.keySet());
|
||||
}
|
||||
|
||||
private CryptoModuleConfigVO getVO(String cryptoName) {
|
||||
CryptoModuleConfigVO vo = configMap.get(cryptoName);
|
||||
if (vo == null) {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.eactive.eai.manage.crypto;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.eactive.eai.common.security.CryptoModuleDiagnosticDTO;
|
||||
|
||||
/**
|
||||
* 암호화모듈(CryptoModuleManager) 진단 및 암복호화 테스트 API.
|
||||
*
|
||||
* 원본 암호키(encKey/decKey/ivHex)는 응답에 포함하지 않는다. 메타데이터와
|
||||
* 키 도출 전략(KeyDerivationStrategy) 로드 상태만 노출한다.
|
||||
*
|
||||
* GET /manage/crypto/list → 등록된 전체 암호화모듈 진단정보
|
||||
* GET /manage/crypto/{cryptoName} → 단일 암호화모듈 진단정보
|
||||
* GET /manage/crypto/cache/dynamic-keys → 동적 키 캐시 키 목록 (원본 키 아님)
|
||||
* POST /manage/crypto/{cryptoName}/test/encrypt → 테스트 암호화 (Base64 입출력)
|
||||
* POST /manage/crypto/{cryptoName}/test/decrypt → 테스트 복호화 (Base64 입출력)
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/manage/crypto")
|
||||
public class CryptoModuleManageController {
|
||||
|
||||
@Autowired
|
||||
private CryptoModuleManageService cryptoModuleManageService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<?> listAll() {
|
||||
List<CryptoModuleDiagnosticDTO> list = cryptoModuleManageService.listAll();
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("data", list);
|
||||
result.put("count", list.size());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@GetMapping("/{cryptoName}")
|
||||
public ResponseEntity<?> get(@PathVariable String cryptoName) {
|
||||
return respond(() -> cryptoModuleManageService.get(cryptoName));
|
||||
}
|
||||
|
||||
@GetMapping("/cache/dynamic-keys")
|
||||
public ResponseEntity<?> listDynamicCacheKeys() {
|
||||
List<String> keys = cryptoModuleManageService.listDynamicCacheKeys();
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("data", keys);
|
||||
result.put("count", keys.size());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@PostMapping("/{cryptoName}/test/encrypt")
|
||||
public ResponseEntity<?> testEncrypt(@PathVariable String cryptoName, @RequestBody CryptoTestRequestDTO request) {
|
||||
return respond(() -> {
|
||||
String cipherTextBase64 = cryptoModuleManageService.testEncrypt(cryptoName,
|
||||
request.getRuntimeContext(), request.getPlainTextBase64(), request.getAadBase64());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("cipherTextBase64", cipherTextBase64);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
@PostMapping("/{cryptoName}/test/decrypt")
|
||||
public ResponseEntity<?> testDecrypt(@PathVariable String cryptoName, @RequestBody CryptoTestRequestDTO request) {
|
||||
return respond(() -> {
|
||||
String plainTextBase64 = cryptoModuleManageService.testDecrypt(cryptoName,
|
||||
request.getRuntimeContext(), request.getCipherTextBase64(), request.getAadBase64());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("plainTextBase64", plainTextBase64);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
private ResponseEntity<?> respond(Callable<Object> action) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
result.put("success", true);
|
||||
result.put("data", action.call());
|
||||
} catch (Exception e) {
|
||||
result.put("success", false);
|
||||
result.put("message", e.getMessage());
|
||||
}
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.eactive.eai.manage.crypto;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.eactive.eai.common.security.CryptoModuleDiagnosticDTO;
|
||||
import com.eactive.eai.common.security.CryptoModuleExtension;
|
||||
import com.eactive.eai.common.security.CryptoModuleManager;
|
||||
|
||||
/**
|
||||
* CryptoModuleManager 진단 및 암복호화 테스트를 위임하는 서비스.
|
||||
*
|
||||
* createExtension(cryptoName, runtimeContext) 는 STATIC 키 방식인 경우 내부에서
|
||||
* createExtension(cryptoName) 으로 위임하므로, 테스트 시 runtimeContext 유무와 무관하게
|
||||
* 동일 메서드를 사용한다.
|
||||
*/
|
||||
@Service
|
||||
public class CryptoModuleManageService {
|
||||
|
||||
public List<CryptoModuleDiagnosticDTO> listAll() {
|
||||
return CryptoModuleManager.getInstance().describeAll();
|
||||
}
|
||||
|
||||
public CryptoModuleDiagnosticDTO get(String cryptoName) {
|
||||
return CryptoModuleManager.getInstance().describe(cryptoName);
|
||||
}
|
||||
|
||||
public List<String> listDynamicCacheKeys() {
|
||||
return CryptoModuleManager.getInstance().listDynamicCacheKeys();
|
||||
}
|
||||
|
||||
public String testEncrypt(String cryptoName, Map<String, String> runtimeContext,
|
||||
String plainTextBase64, String aadBase64) throws Exception {
|
||||
byte[] plain = Base64.getDecoder().decode(plainTextBase64);
|
||||
CryptoModuleExtension ext = CryptoModuleManager.getInstance()
|
||||
.createExtension(cryptoName, runtimeContext(runtimeContext));
|
||||
byte[] cipher = aadBase64 != null
|
||||
? ext.encrypt(plain, Base64.getDecoder().decode(aadBase64))
|
||||
: ext.encrypt(plain);
|
||||
return Base64.getEncoder().encodeToString(cipher);
|
||||
}
|
||||
|
||||
public String testDecrypt(String cryptoName, Map<String, String> runtimeContext,
|
||||
String cipherTextBase64, String aadBase64) throws Exception {
|
||||
byte[] cipherBytes = Base64.getDecoder().decode(cipherTextBase64);
|
||||
CryptoModuleExtension ext = CryptoModuleManager.getInstance()
|
||||
.createExtension(cryptoName, runtimeContext(runtimeContext));
|
||||
byte[] plain = aadBase64 != null
|
||||
? ext.decrypt(cipherBytes, Base64.getDecoder().decode(aadBase64))
|
||||
: ext.decrypt(cipherBytes);
|
||||
return Base64.getEncoder().encodeToString(plain);
|
||||
}
|
||||
|
||||
private Map<String, String> runtimeContext(Map<String, String> runtimeContext) {
|
||||
return runtimeContext != null ? runtimeContext : Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.eactive.eai.manage.crypto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 암복호화 테스트 요청 DTO. 평문/암문은 Base64로 주고받는다.
|
||||
*/
|
||||
@Data
|
||||
public class CryptoTestRequestDTO {
|
||||
|
||||
String plainTextBase64;
|
||||
String cipherTextBase64;
|
||||
String aadBase64;
|
||||
Map<String, String> runtimeContext;
|
||||
}
|
||||
Reference in New Issue
Block a user