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:
@@ -54,11 +54,13 @@ class CryptoModuleServiceTest {
|
||||
SecretKey masterKey = new SecretKeySpec(KEY_128, "AES");
|
||||
when(mockHsm.getSecretKey(anyString())).thenReturn(masterKey);
|
||||
|
||||
CryptoModuleConfig aes = buildStatic("AES_SVC", "AES", "CBC", "PKCS5Padding");
|
||||
CryptoModuleConfig aria = buildStatic("ARIA_SVC", "ARIA", "CBC", "PKCS5Padding");
|
||||
CryptoModuleConfig ecb = buildStaticNoIv("AES_ECB_SVC", "AES", "ECB", "NoPadding");
|
||||
CryptoModuleConfig dyn = buildDynamic("DYN_SVC", "AES", "CBC", "PKCS5Padding");
|
||||
when(mockLoader.findAll()).thenReturn(Arrays.asList(aes, aria, ecb, dyn));
|
||||
CryptoModuleConfig aes = buildStatic("AES_SVC", "AES", "CBC", "PKCS5Padding");
|
||||
CryptoModuleConfig aria = buildStatic("ARIA_SVC", "ARIA", "CBC", "PKCS5Padding");
|
||||
CryptoModuleConfig ecb = buildStaticNoIv("AES_ECB_SVC", "AES", "ECB", "NoPadding");
|
||||
CryptoModuleConfig dyn = buildDynamic("DYN_SVC", "AES", "CBC", "PKCS5Padding");
|
||||
CryptoModuleConfig gcm = buildStatic("AES_GCM_SVC", "AES", "GCM", "NoPadding");
|
||||
CryptoModuleConfig gcmDyn = buildDynamic("DYN_GCM_SVC", "AES", "GCM", "NoPadding");
|
||||
when(mockLoader.findAll()).thenReturn(Arrays.asList(aes, aria, ecb, dyn, gcm, gcmDyn));
|
||||
|
||||
Constructor<CryptoModuleManager> ctor = CryptoModuleManager.class.getDeclaredConstructor();
|
||||
ctor.setAccessible(true);
|
||||
@@ -200,6 +202,124 @@ class CryptoModuleServiceTest {
|
||||
"NoPadding 모드에서 블록 크기 미정렬 평문은 예외가 발생해야 한다");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 4. STATIC AAD — encrypt(name, aad, plain) / decrypt(name, aad, cipher)
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("4-1. STATIC AES/GCM — AAD 명시 encrypt → decrypt 라운드트립")
|
||||
void testStaticGcm_explicitAad_roundTrip() throws Exception {
|
||||
byte[] aad = "request-header-ctx".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] plain = "GCM AAD 서비스 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("AES_GCM_SVC", aad, plain);
|
||||
byte[] decrypted = service.decrypt("AES_GCM_SVC", aad, encrypted);
|
||||
|
||||
assertArrayEquals(plain, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("4-2. STATIC AES/GCM — AAD null이면 IV를 AAD로 사용하는 기본 동작")
|
||||
void testStaticGcm_nullAad_usesIvAsAad() throws Exception {
|
||||
byte[] plain = "AAD null 기본 동작 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("AES_GCM_SVC", (byte[]) null, plain);
|
||||
byte[] decrypted = service.decrypt("AES_GCM_SVC", (byte[]) null, encrypted);
|
||||
|
||||
assertArrayEquals(plain, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("4-3. STATIC AES/GCM — 다른 AAD로 복호화 시 인증 실패 예외")
|
||||
void testStaticGcm_wrongAad_throwsAuthException() throws Exception {
|
||||
byte[] aad = "correct-aad-value".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] wrongAad = "wrong-aad-value!!".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] plain = "GCM 인증 실패 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("AES_GCM_SVC", aad, plain);
|
||||
|
||||
assertThrows(Exception.class,
|
||||
() -> service.decrypt("AES_GCM_SVC", wrongAad, encrypted),
|
||||
"잘못된 AAD로 GCM 복호화 시 인증 실패 예외가 발생해야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("4-4. STATIC CBC — AAD 파라미터는 무시되고 정상 복호화")
|
||||
void testStaticCbc_aadIgnored_normalDecrypt() throws Exception {
|
||||
byte[] aad = "ignored-aad".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] plain = "CBC AAD 무시 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("AES_SVC", aad, plain);
|
||||
byte[] decrypted = service.decrypt("AES_SVC", aad, encrypted);
|
||||
|
||||
assertArrayEquals(plain, decrypted, "CBC 모드에서 AAD는 무시되고 정상 암복호화되어야 한다");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 5. DYNAMIC AAD — encrypt(name, ctx, aad, plain) / decrypt(name, ctx, aad, cipher)
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("5-1. DYNAMIC AES/GCM — runtimeContext + AAD 명시 encrypt → decrypt 라운드트립")
|
||||
void testDynamicGcm_explicitAad_roundTrip() throws Exception {
|
||||
Map<String, String> runtimeCtx = new HashMap<>();
|
||||
runtimeCtx.put("X-Api-Enc-Key", "clientKey0000001");
|
||||
|
||||
byte[] aad = "dynamic-gcm-aad-ctx".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] plain = "DYNAMIC GCM AAD 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("DYN_GCM_SVC", runtimeCtx, aad, plain);
|
||||
byte[] decrypted = service.decrypt("DYN_GCM_SVC", runtimeCtx, aad, encrypted);
|
||||
|
||||
assertArrayEquals(plain, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("5-2. DYNAMIC AES/GCM — AAD null이면 IV를 AAD로 사용하는 기본 동작")
|
||||
void testDynamicGcm_nullAad_usesIvAsAad() throws Exception {
|
||||
Map<String, String> runtimeCtx = new HashMap<>();
|
||||
runtimeCtx.put("X-Api-Enc-Key", "clientKey0000001");
|
||||
|
||||
byte[] plain = "DYNAMIC GCM AAD null 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("DYN_GCM_SVC", runtimeCtx, null, plain);
|
||||
byte[] decrypted = service.decrypt("DYN_GCM_SVC", runtimeCtx, null, encrypted);
|
||||
|
||||
assertArrayEquals(plain, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("5-3. DYNAMIC AES/GCM — 다른 AAD로 복호화 시 인증 실패 예외")
|
||||
void testDynamicGcm_wrongAad_throwsAuthException() throws Exception {
|
||||
Map<String, String> runtimeCtx = new HashMap<>();
|
||||
runtimeCtx.put("X-Api-Enc-Key", "clientKey0000001");
|
||||
|
||||
byte[] aad = "correct-aad-value".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] wrongAad = "wrong-aad-value!!".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] plain = "DYNAMIC GCM 인증 실패".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("DYN_GCM_SVC", runtimeCtx, aad, plain);
|
||||
|
||||
assertThrows(Exception.class,
|
||||
() -> service.decrypt("DYN_GCM_SVC", runtimeCtx, wrongAad, encrypted),
|
||||
"잘못된 AAD로 DYNAMIC GCM 복호화 시 인증 실패 예외가 발생해야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("5-4. DYNAMIC CBC — AAD 파라미터는 무시되고 정상 복호화")
|
||||
void testDynamicCbc_aadIgnored_normalDecrypt() throws Exception {
|
||||
Map<String, String> runtimeCtx = new HashMap<>();
|
||||
runtimeCtx.put("X-Api-Enc-Key", "clientKey0000001");
|
||||
|
||||
byte[] aad = "ignored-aad".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] plain = "DYNAMIC CBC AAD 무시 테스트".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] encrypted = service.encrypt("DYN_SVC", runtimeCtx, aad, plain);
|
||||
byte[] decrypted = service.decrypt("DYN_SVC", runtimeCtx, aad, encrypted);
|
||||
|
||||
assertArrayEquals(plain, decrypted, "DYNAMIC CBC 모드에서 AAD는 무시되고 정상 암복호화되어야 한다");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 헬퍼
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user