feat: CryptoFilter GCM AAD 지원 추가 및 단위테스트 작성
- CryptoFilter: crypto.aad.header 프로퍼티 + buildAad() 메서드 추가 - CryptoFilter: 기본 scope SCOPE_BODY → SCOPE_FIELD 변경 - CryptoFilter: decryptBody/decryptField/encryptBody/encryptField aad 파라미터 적용 - BaseCryptoFilterTest: BODY/FIELD scope, buildAad, AAD 헤더 연동 단위테스트 신규 작성 - CryptoModuleServiceTest: STATIC/DYNAMIC GCM AAD 테스트 섹션 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,19 +35,24 @@ import com.eactive.eai.util.JsonPathUtil;
|
||||
* crypto.dec.to.path 복호화 결과 반영 경로. "/" = 전체 body 교체.
|
||||
* crypto.enc.from.path 암호화 대상 JSON 경로. "/" = 전체 body 암호화.
|
||||
* crypto.enc.to.path 암호화 결과(Base64)를 넣을 JSON 경로 (예: /encryptedData)
|
||||
*
|
||||
* GCM AAD 전용 (선택):
|
||||
* crypto.aad.header AAD로 사용할 HTTP 요청 헤더명 (예: X-Api-Request-Id).
|
||||
* 미설정 또는 헤더값 없으면 aad=null → IV를 AAD 대체값으로 사용.
|
||||
*/
|
||||
public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
|
||||
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
|
||||
|
||||
public static final String PROP_MODULE_NAME = "crypto.module.name";
|
||||
public static final String PROP_SCOPE = "crypto.scope";
|
||||
public static final String PROP_DEC_ENABLED = "crypto.dec.enabled";
|
||||
public static final String PROP_ENC_ENABLED = "crypto.enc.enabled";
|
||||
public static final String PROP_MODULE_NAME = "crypto.module.name";
|
||||
public static final String PROP_SCOPE = "crypto.scope";
|
||||
public static final String PROP_DEC_ENABLED = "crypto.dec.enabled";
|
||||
public static final String PROP_ENC_ENABLED = "crypto.enc.enabled";
|
||||
public static final String PROP_DEC_FROM_PATH = "crypto.dec.from.path";
|
||||
public static final String PROP_DEC_TO_PATH = "crypto.dec.to.path";
|
||||
public static final String PROP_ENC_FROM_PATH = "crypto.enc.from.path";
|
||||
public static final String PROP_ENC_TO_PATH = "crypto.enc.to.path";
|
||||
public static final String PROP_AAD_HEADER = "crypto.aad.header";
|
||||
|
||||
protected static final String SCOPE_BODY = "BODY";
|
||||
protected static final String SCOPE_FIELD = "FIELD";
|
||||
@@ -79,13 +84,14 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
String scope = prop.getProperty(PROP_SCOPE, SCOPE_FIELD).toUpperCase();
|
||||
String body = toBodyString(message);
|
||||
Map<String, String> runtimeCtx = buildRuntimeContext(prop, request);
|
||||
byte[] aad = buildAad(prop, request);
|
||||
|
||||
if (SCOPE_FIELD.equals(scope)) {
|
||||
return decryptField(body, moduleName, runtimeCtx,
|
||||
return decryptField(body, moduleName, runtimeCtx, aad,
|
||||
required(prop, PROP_DEC_FROM_PATH),
|
||||
prop.getProperty(PROP_DEC_TO_PATH, PATH_ROOT));
|
||||
}
|
||||
return decryptBody(body, moduleName, runtimeCtx);
|
||||
return decryptBody(body, moduleName, runtimeCtx, aad);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
@@ -104,22 +110,23 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
String scope = prop.getProperty(PROP_SCOPE, SCOPE_FIELD).toUpperCase();
|
||||
String body = toBodyString(resultMessage);
|
||||
Map<String, String> runtimeCtx = buildRuntimeContext(prop, request);
|
||||
byte[] aad = buildAad(prop, request);
|
||||
|
||||
if (SCOPE_FIELD.equals(scope)) {
|
||||
return encryptField(body, moduleName, runtimeCtx,
|
||||
return encryptField(body, moduleName, runtimeCtx, aad,
|
||||
prop.getProperty(PROP_ENC_FROM_PATH, PATH_ROOT),
|
||||
required(prop, PROP_ENC_TO_PATH));
|
||||
}
|
||||
return encryptBody(body, moduleName, runtimeCtx);
|
||||
return encryptBody(body, moduleName, runtimeCtx, aad);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 복호화 구현
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private String decryptBody(String body, String moduleName, Map<String, String> runtimeCtx) throws Exception {
|
||||
private String decryptBody(String body, String moduleName, Map<String, String> runtimeCtx, byte[] aad) throws Exception {
|
||||
byte[] cipherBytes = Base64.getDecoder().decode(body.trim());
|
||||
byte[] plainBytes = CryptoModuleService.getInstance().decrypt(moduleName, runtimeCtx, cipherBytes);
|
||||
byte[] plainBytes = CryptoModuleService.getInstance().decrypt(moduleName, runtimeCtx, aad, cipherBytes);
|
||||
return new String(plainBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@@ -127,7 +134,7 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
* fromPath의 Base64 값을 복호화하여 toPath에 반영.
|
||||
* toPath = "/" → 복호화된 텍스트로 body 전체 교체.
|
||||
*/
|
||||
private String decryptField(String body, String moduleName, Map<String, String> runtimeCtx,
|
||||
private String decryptField(String body, String moduleName, Map<String, String> runtimeCtx, byte[] aad,
|
||||
String fromPath, String toPath) throws Exception {
|
||||
|
||||
String encBase64 = JsonPathUtil.getValueAtPath(body, fromPath);
|
||||
@@ -137,7 +144,7 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
}
|
||||
|
||||
byte[] cipherBytes = Base64.getDecoder().decode(encBase64.trim());
|
||||
byte[] plainBytes = CryptoModuleService.getInstance().decrypt(moduleName, runtimeCtx, cipherBytes);
|
||||
byte[] plainBytes = CryptoModuleService.getInstance().decrypt(moduleName, runtimeCtx, aad, cipherBytes);
|
||||
String plainText = new String(plainBytes, StandardCharsets.UTF_8);
|
||||
|
||||
if (PATH_ROOT.equals(toPath)) {
|
||||
@@ -150,8 +157,8 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
// 암호화 구현
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private String encryptBody(String body, String moduleName, Map<String, String> runtimeCtx) throws Exception {
|
||||
byte[] cipherBytes = CryptoModuleService.getInstance().encrypt(moduleName, runtimeCtx,
|
||||
private String encryptBody(String body, String moduleName, Map<String, String> runtimeCtx, byte[] aad) throws Exception {
|
||||
byte[] cipherBytes = CryptoModuleService.getInstance().encrypt(moduleName, runtimeCtx, aad,
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(cipherBytes);
|
||||
}
|
||||
@@ -160,7 +167,7 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
* fromPath 값을 암호화하여 toPath(Base64)에 반영.
|
||||
* fromPath = "/" → body 전체를 암호화하여 toPath 필드명으로 래핑.
|
||||
*/
|
||||
private String encryptField(String body, String moduleName, Map<String, String> runtimeCtx,
|
||||
private String encryptField(String body, String moduleName, Map<String, String> runtimeCtx, byte[] aad,
|
||||
String fromPath, String toPath) throws Exception {
|
||||
|
||||
String plainText;
|
||||
@@ -174,7 +181,7 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
}
|
||||
}
|
||||
|
||||
byte[] cipherBytes = CryptoModuleService.getInstance().encrypt(moduleName, runtimeCtx,
|
||||
byte[] cipherBytes = CryptoModuleService.getInstance().encrypt(moduleName, runtimeCtx, aad,
|
||||
plainText.getBytes(StandardCharsets.UTF_8));
|
||||
String encBase64 = Base64.getEncoder().encodeToString(cipherBytes);
|
||||
|
||||
@@ -208,6 +215,19 @@ public abstract class CryptoFilter implements HttpAdapterFilter {
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* crypto.aad.header 프로퍼티에 지정된 헤더값을 UTF-8 바이트로 반환한다.
|
||||
* 프로퍼티 미설정 또는 헤더값 없으면 null을 반환하여 GCM 기본 동작(IV를 AAD 대체값으로 사용)을 따른다.
|
||||
*/
|
||||
protected byte[] buildAad(Properties prop, HttpServletRequest request) {
|
||||
String headerName = prop.getProperty(PROP_AAD_HEADER);
|
||||
if (StringUtils.isBlank(headerName) || request == null) {
|
||||
return null;
|
||||
}
|
||||
String headerValue = request.getHeader(headerName);
|
||||
return StringUtils.isBlank(headerValue) ? null : headerValue.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private boolean isEnabled(Properties prop, String key, String defaultVal) {
|
||||
return !"N".equalsIgnoreCase(prop.getProperty(key, defaultVal));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user