암복호화 function 개발
- 암호화모듈서비스(CryptoModuleService)를 호출하여 암복호화 수행하는 function
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package com.eactive.eai.custom.transformer.function;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.nfunk.jep.ParseException;
|
||||
import org.nfunk.jep.function.PostfixMathCommand;
|
||||
|
||||
import com.eactive.eai.common.security.CryptoModuleService;
|
||||
|
||||
/**
|
||||
* JEP 트랜스포머 함수 — DJB ERP 연동 복호화.
|
||||
*
|
||||
* 사용법: DJBErpDecryt(encBase64, moduleName) DJBErpDecryt(encBase64, moduleName,
|
||||
* groupSeq)
|
||||
*
|
||||
* 파라미터: encBase64 : Base64 인코딩된 암호문 (String) moduleName : DB에 등록된 암호화 모듈명
|
||||
* (String) groupSeq : 키 도출용 그룹 시퀀스 (String, 선택). 미전달 시 빈 context로 호출.
|
||||
*
|
||||
* 반환값: 복호화된 평문 (String)
|
||||
*/
|
||||
public class DJBErpDecryt extends PostfixMathCommand {
|
||||
|
||||
public DJBErpDecryt() {
|
||||
numberOfParameters = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void run(Stack inStack) throws ParseException {
|
||||
checkStack(inStack);
|
||||
|
||||
if (curNumberOfParameters != 2) {
|
||||
throw new ParseException("DJBErpDecryt: 파라미터는 2개개여야 합니다 (encBase64, moduleName");
|
||||
}
|
||||
|
||||
String moduleName = inStack.pop().toString();
|
||||
String encBase64 = inStack.pop().toString();
|
||||
|
||||
try {
|
||||
byte[] cipherBytes = Base64.getDecoder().decode(encBase64.trim());
|
||||
byte[] plainBytes = CryptoModuleService.getInstance().decrypt(moduleName, new HashMap<>(), null, cipherBytes);
|
||||
inStack.push(new String(plainBytes, StandardCharsets.UTF_8));
|
||||
} catch (ParseException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ParseException("DJBErpDecryt 복호화 실패: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.eactive.eai.custom.transformer.function;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.nfunk.jep.ParseException;
|
||||
import org.nfunk.jep.function.PostfixMathCommand;
|
||||
|
||||
import com.eactive.eai.common.security.CryptoModuleService;
|
||||
|
||||
/**
|
||||
* JEP 트랜스포머 함수 — DJB ERP 연동 암호화.
|
||||
*
|
||||
* 사용법: DJBErpEncryt(plainText, moduleName) DJBErpEncryt(plainText, moduleName,
|
||||
* groupSeq)
|
||||
*
|
||||
* 파라미터: plainText : 암호화할 평문 (String) moduleName : DB에 등록된 암호화 모듈명 (String)
|
||||
* groupSeq : 키 도출용 그룹 시퀀스 (String, 선택). 미전달 시 빈 context로 호출.
|
||||
*
|
||||
* 반환값: Base64 인코딩된 암호문 (String)
|
||||
*/
|
||||
public class DJBErpEncryt extends PostfixMathCommand {
|
||||
|
||||
public DJBErpEncryt() {
|
||||
numberOfParameters = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void run(Stack inStack) throws ParseException {
|
||||
checkStack(inStack);
|
||||
|
||||
if (curNumberOfParameters != 2) {
|
||||
throw new ParseException("DJBErpEncryt: 파라미터는 2개여야 합니다 (plainText, moduleName)");
|
||||
}
|
||||
|
||||
String moduleName = inStack.pop().toString();
|
||||
String plainText = inStack.pop().toString();
|
||||
|
||||
try {
|
||||
byte[] cipherBytes = CryptoModuleService.getInstance().encrypt(moduleName, new HashMap<>(), null,
|
||||
plainText.getBytes(StandardCharsets.UTF_8));
|
||||
inStack.push(Base64.getEncoder().encodeToString(cipherBytes));
|
||||
} catch (ParseException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ParseException("DJBErpEncryt 암호화 실패: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
package com.eactive.eai.custom.transformer.function;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.nfunk.jep.ParseException;
|
||||
import org.nfunk.jep.function.PostfixMathCommand;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import com.eactive.eai.common.security.CryptoModuleService;
|
||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
|
||||
@TestMethodOrder(MethodOrderer.DisplayName.class)
|
||||
class DJBErpCryptoFunctionTest {
|
||||
|
||||
private static final String MODULE_NAME = "TEST_MODULE";
|
||||
private static final String PLAIN_TEXT = "plaintext-value";
|
||||
private static final byte[] PLAIN_BYTES = PLAIN_TEXT.getBytes(StandardCharsets.UTF_8);
|
||||
private static final byte[] CIPHER_BYTES = new byte[]{0x10, 0x20, 0x30, 0x40};
|
||||
private static final String ENC_BASE64 = Base64.getEncoder().encodeToString(CIPHER_BYTES);
|
||||
|
||||
private static GenericApplicationContext ctx;
|
||||
private static CryptoModuleService mockService;
|
||||
|
||||
private DJBErpDecryt decryt;
|
||||
private DJBErpEncryt encryt;
|
||||
|
||||
// =========================================================================
|
||||
// 클래스 초기화
|
||||
// =========================================================================
|
||||
|
||||
@BeforeAll
|
||||
static void setUpClass() throws Exception {
|
||||
mockService = mock(CryptoModuleService.class);
|
||||
|
||||
ctx = new GenericApplicationContext();
|
||||
ctx.getBeanFactory().registerSingleton("cryptoModuleService", mockService);
|
||||
ctx.registerBeanDefinition("applicationContextProvider",
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ApplicationContextProvider.class)
|
||||
.getBeanDefinition());
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownClass() {
|
||||
if (ctx != null) ctx.close();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
reset(mockService);
|
||||
when(mockService.decrypt(any(), anyMap(), isNull(), any(byte[].class))).thenReturn(PLAIN_BYTES);
|
||||
when(mockService.encrypt(any(), anyMap(), isNull(), any(byte[].class))).thenReturn(CIPHER_BYTES);
|
||||
decryt = new DJBErpDecryt();
|
||||
encryt = new DJBErpEncryt();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 1. DJBErpDecryt
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("1-1. 정상 복호화: (encBase64, moduleName) → 평문이 스택에 push된다")
|
||||
void decryt_normalCase_pushesPlainText() throws Exception {
|
||||
Stack<Object> stack = buildStack(ENC_BASE64, MODULE_NAME);
|
||||
setParamCount(decryt, 2);
|
||||
|
||||
decryt.run(stack);
|
||||
|
||||
assertEquals(PLAIN_TEXT, stack.pop());
|
||||
assertTrue(stack.isEmpty(), "결과 push 후 스택에 잔여 항목이 없어야 한다");
|
||||
verify(mockService).decrypt(eq(MODULE_NAME), anyMap(), isNull(), any(byte[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-2. 파라미터 수가 2가 아니면 ParseException")
|
||||
void decryt_wrongParamCount_throwsParseException() throws Exception {
|
||||
Stack<Object> stack = buildStack(ENC_BASE64, MODULE_NAME);
|
||||
setParamCount(decryt, 3);
|
||||
|
||||
assertThrows(ParseException.class, () -> decryt.run(stack));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-3. 유효하지 않은 Base64 입력 → ParseException")
|
||||
void decryt_invalidBase64_throwsParseException() throws Exception {
|
||||
Stack<Object> stack = buildStack("not-valid-base64!!!", MODULE_NAME);
|
||||
setParamCount(decryt, 2);
|
||||
|
||||
assertThrows(ParseException.class, () -> decryt.run(stack));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-4. 암호화 서비스 예외 → 'DJBErpDecryt 복호화 실패' ParseException으로 래핑")
|
||||
void decryt_serviceThrows_wrapsInParseException() throws Exception {
|
||||
when(mockService.decrypt(any(), anyMap(), isNull(), any(byte[].class)))
|
||||
.thenThrow(new RuntimeException("모듈 오류"));
|
||||
Stack<Object> stack = buildStack(ENC_BASE64, MODULE_NAME);
|
||||
setParamCount(decryt, 2);
|
||||
|
||||
ParseException ex = assertThrows(ParseException.class, () -> decryt.run(stack));
|
||||
assertTrue(ex.getMessage().contains("DJBErpDecryt 복호화 실패"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("1-5. moduleName이 CryptoModuleService에 그대로 전달된다")
|
||||
void decryt_moduleNamePassedToService() throws Exception {
|
||||
String customModule = "CUSTOM_AES_MODULE";
|
||||
Stack<Object> stack = buildStack(ENC_BASE64, customModule);
|
||||
setParamCount(decryt, 2);
|
||||
|
||||
decryt.run(stack);
|
||||
|
||||
verify(mockService).decrypt(eq(customModule), anyMap(), isNull(), any(byte[].class));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 2. DJBErpEncryt
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@DisplayName("2-1. 정상 암호화: (plainText, moduleName) → Base64가 스택에 push된다")
|
||||
void encryt_normalCase_pushesBase64() throws Exception {
|
||||
Stack<Object> stack = buildStack(PLAIN_TEXT, MODULE_NAME);
|
||||
setParamCount(encryt, 2);
|
||||
|
||||
encryt.run(stack);
|
||||
|
||||
assertEquals(ENC_BASE64, stack.pop());
|
||||
assertTrue(stack.isEmpty(), "결과 push 후 스택에 잔여 항목이 없어야 한다");
|
||||
verify(mockService).encrypt(eq(MODULE_NAME), anyMap(), isNull(), any(byte[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("2-2. 파라미터 수가 2가 아니면 ParseException")
|
||||
void encryt_wrongParamCount_throwsParseException() throws Exception {
|
||||
Stack<Object> stack = buildStack(PLAIN_TEXT, MODULE_NAME);
|
||||
setParamCount(encryt, 3);
|
||||
|
||||
assertThrows(ParseException.class, () -> encryt.run(stack));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("2-3. 암호화 서비스 예외 → 'DJBErpEncryt 암호화 실패' ParseException으로 래핑")
|
||||
void encryt_serviceThrows_wrapsInParseException() throws Exception {
|
||||
when(mockService.encrypt(any(), anyMap(), isNull(), any(byte[].class)))
|
||||
.thenThrow(new RuntimeException("모듈 오류"));
|
||||
Stack<Object> stack = buildStack(PLAIN_TEXT, MODULE_NAME);
|
||||
setParamCount(encryt, 2);
|
||||
|
||||
ParseException ex = assertThrows(ParseException.class, () -> encryt.run(stack));
|
||||
assertTrue(ex.getMessage().contains("DJBErpEncryt 암호화 실패"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("2-4. moduleName이 CryptoModuleService에 그대로 전달된다")
|
||||
void encryt_moduleNamePassedToService() throws Exception {
|
||||
String customModule = "CUSTOM_AES_MODULE";
|
||||
Stack<Object> stack = buildStack(PLAIN_TEXT, customModule);
|
||||
setParamCount(encryt, 2);
|
||||
|
||||
encryt.run(stack);
|
||||
|
||||
verify(mockService).encrypt(eq(customModule), anyMap(), isNull(), any(byte[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("2-5. plainText가 UTF-8 바이트로 변환되어 서비스에 전달된다")
|
||||
void encryt_plainTextEncodedAsUtf8() throws Exception {
|
||||
String korean = "한글평문";
|
||||
Stack<Object> stack = buildStack(korean, MODULE_NAME);
|
||||
setParamCount(encryt, 2);
|
||||
|
||||
encryt.run(stack);
|
||||
|
||||
verify(mockService).encrypt(eq(MODULE_NAME), anyMap(), isNull(),
|
||||
eq(korean.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 헬퍼
|
||||
// =========================================================================
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private Stack<Object> buildStack(Object... params) {
|
||||
Stack stack = new Stack();
|
||||
for (Object p : params) {
|
||||
stack.push(p);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
private void setParamCount(PostfixMathCommand cmd, int count) throws Exception {
|
||||
Field field = PostfixMathCommand.class.getDeclaredField("curNumberOfParameters");
|
||||
field.setAccessible(true);
|
||||
field.set(cmd, count);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user