bundle 병합 문제 수정
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
SafeDBSDK_v4.0.19.jar 파일은 빌드결과에 포함되지 않고 compileOnly 로만 사용
|
||||
실제 라이브러리는 WAS classpath 로 지정
|
||||
Binary file not shown.
@@ -1,94 +0,0 @@
|
||||
package com.eactive.ext.kjb.safedb;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* SafeDBFaker provides a fake encryption method for testing purposes when the real SafeDB library is not available.
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class SafeDBFaker {
|
||||
private static final String AES_256_KEY = "1234567890123456" + "1234567890123456";
|
||||
private static final byte[] IV = "1234567890123456".getBytes();
|
||||
|
||||
|
||||
public byte[] encrypt(String userId, String table, String column, byte[] data) {
|
||||
if (SafeDBColumns.PSWD.isEqualsIgnoreCase(column)) {
|
||||
// SHA256 + Base64 인코딩된 가짜 비밀번호 반환
|
||||
return sha256(data);
|
||||
} else {
|
||||
// AES256 암호화된 가짜 데이터 반환
|
||||
return aes256Encrypt(data);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decrypt(String userId, String table, String column, byte[] data) {
|
||||
if (SafeDBColumns.PSWD.isEqualsIgnoreCase(column)) {
|
||||
// 비밀번호는 복호화하지 않음
|
||||
throw new UnsupportedOperationException("Password decryption is not supported.");
|
||||
} else {
|
||||
// AES256 복호화된 가짜 데이터 반환
|
||||
return aes256Decrypt(data);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] sha256(byte[] data) {
|
||||
MessageDigest digest = null;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
return digest.digest(data);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Error generating SHA-256 hash", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String sha256Base64(String data) {
|
||||
return Base64.getEncoder().encodeToString(this.sha256(data.getBytes()));
|
||||
}
|
||||
|
||||
// AES256 암호화
|
||||
private byte[] aes256Encrypt(byte[] data) {
|
||||
try {
|
||||
Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(AES_256_KEY.getBytes(), "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(IV));
|
||||
return cipher.doFinal(data);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error during AES encryption", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAes256KeyString(String data) {
|
||||
return Base64.getEncoder().encodeToString(this.aes256Encrypt(data.getBytes()));
|
||||
}
|
||||
|
||||
private byte[] aes256Decrypt(byte[] encryptedData) {
|
||||
try {
|
||||
Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(AES_256_KEY.getBytes(), "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(IV));
|
||||
log.debug("encryptedData : {}", Arrays.toString(encryptedData));
|
||||
log.debug("encryptedData toString() : {}", new String(encryptedData));
|
||||
return cipher.doFinal(encryptedData);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error during AES decryption", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String encryptString(String userId, String table, String column, String data) {
|
||||
// return new String(this.encrypt(safedb, s, notRnno, data.getBytes()));
|
||||
return Base64Utils.encodeToString(this.encrypt(userId, table, column, data.getBytes()));
|
||||
}
|
||||
|
||||
protected String decryptString(String userId, String table, String column, String data) {
|
||||
return new String(this.decrypt(userId, table, column, Base64Utils.decodeFromString(data)));
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package com.eactive.ext.kjb.safedb;
|
||||
|
||||
import com.initech.safedb.SimpleSafeDB;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.repository.init.ResourceReader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
@Slf4j
|
||||
public class SafeDBWrapper extends SafeDBBroker {
|
||||
private static SafeDBWrapper instance;
|
||||
|
||||
private SimpleSafeDB safedb;
|
||||
@Getter
|
||||
private boolean isRealSafeDB = false;
|
||||
|
||||
|
||||
public static SafeDBWrapper getInstance() {
|
||||
if ( instance == null ) {
|
||||
synchronized ( SafeDBWrapper.class ) {
|
||||
if ( instance == null ) {
|
||||
instance = new SafeDBWrapper();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static boolean isBase64(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String base64Pattern = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$";
|
||||
return str.matches(base64Pattern);
|
||||
}
|
||||
|
||||
|
||||
private SafeDBWrapper() throws SafeDBWrapperException {
|
||||
synchronized ( SafeDBWrapper.class ) {
|
||||
// "com.initech.safedb.SimpleSafeDB" 클래스 존재 여부 확인
|
||||
try {
|
||||
Class.forName("com.initech.safedb.SimpleSafeDB");
|
||||
|
||||
try (InputStream is = ResourceReader.class.getClassLoader().getResourceAsStream("SafeDBMap_config.xml")) {
|
||||
if (is == null) {
|
||||
isRealSafeDB = false;
|
||||
return;
|
||||
} else {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
|
||||
br.lines().forEach(log::debug);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.safedb = SimpleSafeDB.getInstance();
|
||||
|
||||
if (safedb.login()) {
|
||||
log.debug("[SafeDBWrapper] isLoginCheck : {}", safedb.getSafeDBConfigMgr().isLoginCheck());
|
||||
} else {
|
||||
log.warn("[SafeDBWrapper] isLoginCheck : {}", safedb.getSafeDBConfigMgr().isLoginCheck());
|
||||
}
|
||||
|
||||
isRealSafeDB = true; // 클래스가 존재하면 true로 설정
|
||||
} catch (Exception e) {
|
||||
isRealSafeDB = false; // 클래스가 존재하지 않으면 false로 설정
|
||||
log.info("SafeDB 클래스 로딩 실패 - 로컬 환경이거나 설정 오류 / Fake 모드로 동작함", e);
|
||||
}
|
||||
|
||||
if (isRealSafeDB) {
|
||||
try {
|
||||
super.initialize();
|
||||
} catch (Exception e) {
|
||||
throw new SafeDBWrapperException("[SafeDBWrapper] Initialization error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encrypt(String userId, String table, String column, byte[] data) throws SafeDBWrapperSdkException {
|
||||
if (isRealSafeDB) {
|
||||
try {
|
||||
log.debug("[SafeDBWrapper] real / userId : {}, table : {}, column : {}, data : {}", userId, table, column, data);
|
||||
return safedb.encrypt(userId, table, column, data);
|
||||
} catch (Exception e) {
|
||||
throw new SafeDBWrapperSdkException("[SafeDBWrapper] Encrypt error: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
return super.encrypt(userId, table, column, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encryptString(String userId, String tables, String column, String data) {
|
||||
if (isRealSafeDB) {
|
||||
try {
|
||||
return this.safedb.encryptString(userId, tables, column, data);
|
||||
} catch (Exception e) {
|
||||
throw new SafeDBWrapperSdkException("[SafeDBWrapper] Encrypt error: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
return super.encryptString(userId, tables, column, data);
|
||||
}
|
||||
}
|
||||
|
||||
public String encryptNotRnnoString(String data) throws SafeDBWrapperSdkException {
|
||||
return this.encryptString("SAFEDB", "SAFEDB.POLICY", "NOT_RNNO", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decrypt(String userId, String table, String column, byte[] data) throws SafeDBWrapperSdkException {
|
||||
if (isRealSafeDB) {
|
||||
try {
|
||||
return safedb.decrypt(userId, table, column, data);
|
||||
} catch (Exception e) {
|
||||
throw new SafeDBWrapperException("[SafeDBWrapper] Decrypt error: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
return super.decrypt(userId, table, column, data);
|
||||
}
|
||||
}
|
||||
|
||||
public String decryptString(String userId, String table, String column, String data) throws SafeDBWrapperSdkException {
|
||||
if (isRealSafeDB) {
|
||||
try {
|
||||
return safedb.decryptString(userId, table, column, data);
|
||||
} catch (Exception e) {
|
||||
throw new SafeDBWrapperException("[SafeDBWrapper] Decrypt error: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
return super.decryptString(userId, table, column, data);
|
||||
}
|
||||
}
|
||||
|
||||
public String decryptNotRnno(String data) throws SafeDBWrapperSdkException {
|
||||
return this.decryptString("SAFEDB", "SAFEDB.POLICY", "NOT_RNNO", data);
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package com.eactive.ext.kjb.safedb.test;
|
||||
|
||||
import com.eactive.ext.kjb.safedb.SafeDBWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@Slf4j
|
||||
public class SafeDbWrapperTest {
|
||||
|
||||
@Test
|
||||
public void fakeSafeDBTest() {
|
||||
log.info("FakeSafeDBWrapper Length Validation Start");
|
||||
|
||||
if ("true".equalsIgnoreCase(System.getenv("safedb"))) {
|
||||
log.info("실제 Safedb 테스트를 위해 테스트 종료 처리");
|
||||
assertTrue("true".equalsIgnoreCase(System.getenv("safedb")));
|
||||
return;
|
||||
}
|
||||
|
||||
SafeDBWrapper wrapper;
|
||||
try {
|
||||
wrapper = SafeDBWrapper.getInstance();
|
||||
} catch (Exception e) {
|
||||
// classloader 에러인지 initialization 에러인지 구분
|
||||
if (e.getMessage().contains("Classloader error")) {
|
||||
throw new RuntimeException("SafeDBWrapper classloader error: " + e.getMessage(), e);
|
||||
} else {
|
||||
// initialization 에러(로컬에서는 동작하지 않는다 = 정상 처리)
|
||||
log.info("SafeDBWrapper initialization error (expected in local environment): " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (wrapper.isRealSafeDB()) {
|
||||
log.info("This test is for FakeSafeDBWrapper only. Skipping test.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (int len = 1; len <= 255; len++) {
|
||||
String originalData = repeat("A", len); // JDK8 호환 반복 문자열
|
||||
try {
|
||||
log.debug("OriginalData: {}", originalData);
|
||||
|
||||
// 암호화 (Base64 인코딩 결과 가정)
|
||||
String encryptedData = wrapper.encryptNotRnnoString(originalData);
|
||||
log.debug("Encrypted Data: {}", encryptedData);
|
||||
|
||||
// 예상 암호문 길이 계산
|
||||
int expectedLen = getExpectedCipherLength(len, true);
|
||||
|
||||
// 복호화
|
||||
String decryptedData = wrapper.decryptNotRnno(encryptedData);
|
||||
log.debug("Decrypted Data: {}", decryptedData);
|
||||
assertEquals(originalData, decryptedData, "Decrypted data does not match original at plain length=" + len);
|
||||
|
||||
// 로그 출력
|
||||
log.info(String.format("Plain=%3d, Encrypted=%3d, Expected=%3d %s",
|
||||
len, encryptedData.length(), expectedLen,
|
||||
(encryptedData.length() == expectedLen ? "✔" : "❌")));
|
||||
|
||||
// 검증
|
||||
// "Length mismatch at plain length="
|
||||
assertEquals(len, expectedLen, encryptedData.length());
|
||||
|
||||
|
||||
// 암복호화 로그 출력
|
||||
log.info(String.format("Original: %s", originalData) +
|
||||
String.format("\nEncrypted: %s", encryptedData) +
|
||||
String.format("\nDecrypted: %s", decryptedData) + "\n");
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error at plain length=" + len, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
@Test
|
||||
public void safeDBTest() {
|
||||
// safedb 프러퍼티가 활성화 일때만 동작
|
||||
if (!"true".equalsIgnoreCase(System.getProperty("safedb"))) {
|
||||
log.info("gradle : -Psafedb 일때만 동작 - {}", System.getProperty("safedb"));
|
||||
return;
|
||||
}
|
||||
|
||||
SafeDBWrapper wrapper;
|
||||
try {
|
||||
wrapper = SafeDBWrapper.getInstance();
|
||||
} catch (Exception e) {
|
||||
log.error("SafeDBWrapper 활성화 실패");
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wrapper.isRealSafeDB()) {
|
||||
log.error("실제 Safedb 로 동작하지 않음");
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
String value = "한글암호화테스트";
|
||||
log.info("Original : {}", value);
|
||||
|
||||
try {
|
||||
String encryptValue = wrapper.encryptNotRnnoString(value);
|
||||
log.info("Encrypted : {}", encryptValue);
|
||||
|
||||
String decryptedValue = wrapper.decryptNotRnno(encryptValue);
|
||||
log.info("Decrypted : {}", decryptedValue);
|
||||
|
||||
assertEquals(value, decryptedValue);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
assertTrue(true);
|
||||
}
|
||||
/**
|
||||
* 암호문 예상 길이 계산 (AES/SEED/ARIA CBC 기준, PKCS5Padding)
|
||||
*/
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private int getExpectedCipherLength(int plainLength, boolean base64Encoding) {
|
||||
int blockSize = 16;
|
||||
int paddedLength = ((plainLength / blockSize) + 1) * blockSize;
|
||||
|
||||
if (base64Encoding) {
|
||||
return (int) (Math.ceil(paddedLength / 3.0) * 4);
|
||||
} else {
|
||||
return paddedLength;
|
||||
}
|
||||
}
|
||||
|
||||
/** JDK8에서 문자열 반복 (String.repeat 대체) */
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private String repeat(String s, int count) {
|
||||
StringBuilder sb = new StringBuilder(s.length() * count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user