PTL_USER 암호화 적용, SafeDB Wrapper 클래스 추가
This commit is contained in:
@@ -71,6 +71,7 @@ local.properties
|
|||||||
.idea
|
.idea
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
*.iml
|
||||||
|
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
|
|||||||
+13
-6
@@ -22,12 +22,6 @@ def hibernateVersion = "5.6.15.Final"
|
|||||||
def generatedJavaDir = "$buildDir/generated/java"
|
def generatedJavaDir = "$buildDir/generated/java"
|
||||||
def generatedTestJavaDir = "$buildDir/generated-test/java"
|
def generatedTestJavaDir = "$buildDir/generated-test/java"
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
url "${nexusUrl}/repository/maven-public/"
|
|
||||||
allowInsecureProtocol = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
@@ -112,6 +106,11 @@ dependencies {
|
|||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.8.2'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.8.2'
|
||||||
|
|
||||||
|
|
||||||
|
// 광주은행 safedb 용 임시 라이브러리
|
||||||
|
compileOnly(fileTree(dir: "tmp/safedb", include: ["*.jar"]))
|
||||||
|
// implementation(fileTree(dir: "tmp/safedb", include: ["*.jar"]))
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@@ -187,3 +186,11 @@ eclipse {
|
|||||||
tasks.named('eclipse').configure {
|
tasks.named('eclipse').configure {
|
||||||
finalizedBy 'compileJava', 'compileTestJava'
|
finalizedBy 'compileJava', 'compileTestJava'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
generatedSourceDirs.add(file(generatedJavaDir))
|
||||||
|
generatedSourceDirs.add(file(generatedTestJavaDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,13 +5,14 @@ import com.eactive.apim.portal.portalorg.entity.PortalOrg;
|
|||||||
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.ApprovalStatus;
|
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.ApprovalStatus;
|
||||||
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.RoleCode;
|
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.RoleCode;
|
||||||
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.UserStatus;
|
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums.UserStatus;
|
||||||
import java.io.Serializable;
|
import com.eactive.ext.kjb.safedb.SafeDBMNotRnnoConverter;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.hibernate.annotations.Comment;
|
import org.hibernate.annotations.Comment;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -28,8 +29,9 @@ public class PortalUser extends Auditable implements Serializable, com.eactive.e
|
|||||||
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
@GeneratedValue(generator = "uuid-gen", strategy = GenerationType.IDENTITY)
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Column(name = "login_id", length = 32, nullable = false)
|
@Column(name = "login_id", length = 200, nullable = false)
|
||||||
@Comment("로그인ID")
|
@Comment("로그인ID")
|
||||||
|
@Convert(converter = SafeDBMNotRnnoConverter.class)
|
||||||
private String loginId;
|
private String loginId;
|
||||||
|
|
||||||
@Column(name = "password_hash", length = 60)
|
@Column(name = "password_hash", length = 60)
|
||||||
@@ -40,16 +42,19 @@ public class PortalUser extends Auditable implements Serializable, com.eactive.e
|
|||||||
@Comment("사용자이름")
|
@Comment("사용자이름")
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
@Column(name = "phone_number", length = 12)
|
@Column(name = "phone_number", length = 50)
|
||||||
@Comment("전화번호")
|
@Comment("전화번호")
|
||||||
|
@Convert(converter = SafeDBMNotRnnoConverter.class)
|
||||||
private String phoneNumber;
|
private String phoneNumber;
|
||||||
|
|
||||||
@Column(name = "mobile_number", length = 20)
|
@Column(name = "mobile_number", length = 50)
|
||||||
@Comment("휴대폰 번호")
|
@Comment("휴대폰 번호")
|
||||||
|
@Convert(converter = SafeDBMNotRnnoConverter.class)
|
||||||
private String mobileNumber;
|
private String mobileNumber;
|
||||||
|
|
||||||
@Column(name = "email_addr", length = 30)
|
@Column(name = "email_addr", length = 200)
|
||||||
@Comment("이메일주소")
|
@Comment("이메일주소")
|
||||||
|
@Convert(converter = SafeDBMNotRnnoConverter.class)
|
||||||
private String emailAddr;
|
private String emailAddr;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
public abstract class SafeDBBroker extends SafeDBFaker{
|
||||||
|
private Object instance;
|
||||||
|
|
||||||
|
protected void initialize() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> clazz = Class.forName("com.initech.safedb.SimpleSafeDB");
|
||||||
|
instance = clazz.getMethod("getInstance").invoke(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object methodInvoke(String methodName, Class<?>[] paramTypes, Object[] params) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
if (instance == null) {
|
||||||
|
throw new IllegalStateException("SafeDBBroker is not initialized.");
|
||||||
|
}
|
||||||
|
return instance.getClass().getMethod(methodName, paramTypes).invoke(instance, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
public enum SafeDBColumns {
|
||||||
|
RNNO("RNNO"),
|
||||||
|
PSWD("PSWD"),
|
||||||
|
NOT_RNNO("NOT_RNNO"),;
|
||||||
|
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
SafeDBColumns (String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean contains(String test) {
|
||||||
|
for (SafeDBColumns c : SafeDBColumns.values()) {
|
||||||
|
if (c.name().equals(test)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEqualsIgnoreCase(SafeDBColumns column, String test) {
|
||||||
|
if (column == null || test == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return column.getValue().equalsIgnoreCase(test);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SafeDBColumns fromString(String text) {
|
||||||
|
for (SafeDBColumns b : SafeDBColumns.values()) {
|
||||||
|
if (b.name().equalsIgnoreCase(text)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("No constant with text " + text + " found");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isEqualsIgnoreCase(String test) {
|
||||||
|
if (test == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.value.equalsIgnoreCase(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SafeDBFaker provides a fake encryption method for testing purposes when the real SafeDB library is not available.
|
||||||
|
*/
|
||||||
|
public abstract class SafeDBFaker {
|
||||||
|
private static final String AES_256_KEY = "1234567890123456" + "1234567890123456";
|
||||||
|
|
||||||
|
|
||||||
|
public String fakeEncrypt(String userId, String table, String column, String data) {
|
||||||
|
if (SafeDBColumns.PSWD.isEqualsIgnoreCase(column)) {
|
||||||
|
// SHA256 + Base64 인코딩된 가짜 비밀번호 반환
|
||||||
|
return sha256Base64(data);
|
||||||
|
} else {
|
||||||
|
// AES256 암호화된 가짜 데이터 반환
|
||||||
|
return aes256Encrypt(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fakeDecrypt(String userId, String table, String column, String data) {
|
||||||
|
if (SafeDBColumns.PSWD.isEqualsIgnoreCase(column)) {
|
||||||
|
// 비밀번호는 복호화하지 않음
|
||||||
|
throw new UnsupportedOperationException("Password decryption is not supported.");
|
||||||
|
} else {
|
||||||
|
// AES256 복호화된 가짜 데이터 반환
|
||||||
|
return aes256Decrypt(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String sha256Base64(String data) {
|
||||||
|
try {
|
||||||
|
MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
|
||||||
|
|
||||||
|
// JVM 옵션을 따라가기 위해 캐릭터셋을 명시 하지 않음.
|
||||||
|
byte[] hash = digest.digest(data.getBytes());
|
||||||
|
|
||||||
|
return java.util.Base64.getEncoder().encodeToString(hash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error generating SHA-256 hash", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AES256 암호화
|
||||||
|
private String aes256Encrypt(String data) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = javax.crypto.Cipher.getInstance("AES");
|
||||||
|
SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(AES_256_KEY.getBytes(), "AES");
|
||||||
|
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keySpec);
|
||||||
|
byte[] encrypted = cipher.doFinal(data.getBytes());
|
||||||
|
return java.util.Base64.getEncoder().encodeToString(encrypted);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error during AES encryption", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String aes256Decrypt(String encryptedData) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = javax.crypto.Cipher.getInstance("AES");
|
||||||
|
SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(AES_256_KEY.getBytes(), "AES");
|
||||||
|
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keySpec);
|
||||||
|
byte[] decodedBytes = java.util.Base64.getDecoder().decode(encryptedData);
|
||||||
|
byte[] decrypted = cipher.doFinal(decodedBytes);
|
||||||
|
return new String(decrypted);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error during AES decryption", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
|
@Converter
|
||||||
|
@Slf4j
|
||||||
|
public class SafeDBMNotRnnoConverter implements AttributeConverter<String, String> {
|
||||||
|
/**
|
||||||
|
* 암호화
|
||||||
|
* @param attribute the entity attribute value to be converted
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(String attribute) {
|
||||||
|
log.debug("DB 암호화 #1 - {} / {}", attribute, SafeDBColumns.NOT_RNNO);
|
||||||
|
|
||||||
|
String originalAttribute = attribute;
|
||||||
|
if (StringUtils.isNotEmpty(attribute)) {
|
||||||
|
SafeDBWrapper safeDBWrapper = SafeDBWrapper.getInstance();
|
||||||
|
attribute = safeDBWrapper.encryptNotRnno(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("DB 암호화 #2 : {} -> {}", originalAttribute, attribute);
|
||||||
|
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 복호화
|
||||||
|
* @param dbData the data from the database column to be
|
||||||
|
* converted
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String convertToEntityAttribute(String dbData) {
|
||||||
|
log.debug("DB 복호화 #1 - {} / {}", dbData, SafeDBColumns.NOT_RNNO);
|
||||||
|
|
||||||
|
String dbDataOriginal = dbData;
|
||||||
|
if (StringUtils.isNotEmpty(dbData)) {
|
||||||
|
if (this.isEncrypted(dbData)) {
|
||||||
|
SafeDBWrapper safeDBWrapper = SafeDBWrapper.getInstance();
|
||||||
|
dbData = safeDBWrapper.decryptNotRnno(dbData);
|
||||||
|
} else {
|
||||||
|
log.debug("DB 복호화 경고: Base64 형식이 아님. 복호화하지 않고 원본 반환. dbData={}", dbData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("DB 복호화 #2 : {} -> {}", dbDataOriginal, dbData);
|
||||||
|
|
||||||
|
return dbData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 암호화 여부 확인(base64 인코딩 여부로 판단, 또는 0-9, - 로 구성된 경우 암호화 되지 않은 걸로 판단(연락처))
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean isEncrypted(String data) {
|
||||||
|
if (StringUtils.isEmpty(data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = data.trim();
|
||||||
|
|
||||||
|
// Base64 형식 체크
|
||||||
|
if (SafeDBWrapper.isBase64(data)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 숫자, - 로만 구성된 경우 암호화 되지 않은 걸로 판단 (ex: 연락처)
|
||||||
|
if (data.matches("^[0-9-]+$")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
public class SafeDBWrapper extends SafeDBBroker {
|
||||||
|
private static SafeDBWrapper instance;
|
||||||
|
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");
|
||||||
|
isRealSafeDB = true; // 클래스가 존재하면 true로 설정
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
isRealSafeDB = false; // 클래스가 존재하지 않으면 false로 설정
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SafeDBWrapperException(("[SafeDBWrapper] Classloader error: " + e.getMessage()), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRealSafeDB) {
|
||||||
|
try {
|
||||||
|
super.initialize();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SafeDBWrapperException("[SafeDBWrapper] Initialization error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String encrypt(String userId, String table, String column, String data) throws SafeDBWrapperSdkException {
|
||||||
|
if (isRealSafeDB) {
|
||||||
|
try {
|
||||||
|
return (String) super.methodInvoke("encrypt",
|
||||||
|
new Class<?>[]{String.class, String.class, String.class, String.class},
|
||||||
|
new Object[]{userId, table, column, data});
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SafeDBWrapperSdkException("[SafeDBWrapper] Encrypt error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return super.fakeEncrypt(userId, table, column, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encryptNotRnno(String data) throws SafeDBWrapperSdkException {
|
||||||
|
return this.encrypt("SAFEDB", "SAFEDB.POLICY", "NOT_RNNO", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String decrypt(String userId, String table, String column, String data) throws SafeDBWrapperSdkException {
|
||||||
|
if (isRealSafeDB) {
|
||||||
|
try {
|
||||||
|
return (String) super.methodInvoke("decrypt",
|
||||||
|
new Class<?>[]{String.class, String.class, String.class, String.class},
|
||||||
|
new Object[]{userId, table, column, data});
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SafeDBWrapperException("[SafeDBWrapper] Decrypt error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return super.fakeDecrypt(userId, table, column, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String decryptNotRnno(String data) throws SafeDBWrapperSdkException {
|
||||||
|
return this.decrypt("SAFEDB", "SAFEDB.POLICY", "NOT_RNNO", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRealSafeDB() {
|
||||||
|
return isRealSafeDB;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
public class SafeDBWrapperException extends RuntimeException {
|
||||||
|
private Exception cause;
|
||||||
|
private int errcode;
|
||||||
|
|
||||||
|
|
||||||
|
public SafeDBWrapperException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SafeDBWrapperException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperException(String message, Exception cause) {
|
||||||
|
super(message);
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperException(int errcode, String message) {
|
||||||
|
super(message);
|
||||||
|
this.errcode = errcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperException(String errcode, String message) {
|
||||||
|
super(message);
|
||||||
|
this.errcode = new Integer(errcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Throwable getCause() {
|
||||||
|
return this.cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return this.cause != null ? super.getMessage() + "\n caused by [ " + this.cause.getMessage() + " ]" : super.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorCode() {
|
||||||
|
return this.errcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb;
|
||||||
|
|
||||||
|
public class SafeDBWrapperSdkException extends SafeDBWrapperException {
|
||||||
|
public SafeDBWrapperSdkException(Exception ex) {
|
||||||
|
super(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperSdkException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperSdkException(String message, Exception ex) {
|
||||||
|
super(message, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeDBWrapperSdkException() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.eactive.ext.kjb.safedb.test;
|
||||||
|
|
||||||
|
import com.eactive.ext.kjb.safedb.SafeDBWrapper;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class SafeDbWrapperTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void faceSafeDBTest() {
|
||||||
|
log("FakeSafeDBWrapper Length Validation Start");
|
||||||
|
|
||||||
|
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("SafeDBWrapper initialization error (expected in local environment): " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapper.isRealSafeDB()) {
|
||||||
|
log("This test is for FakeSafeDBWrapper only. Skipping test.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int len = 1; len <= 255; len++) {
|
||||||
|
String originalData = repeat("A", len); // JDK8 호환 반복 문자열
|
||||||
|
try {
|
||||||
|
// 암호화 (Base64 인코딩 결과 가정)
|
||||||
|
String encryptedData = wrapper.encrypt("userId", "tableName", "columnName", originalData);
|
||||||
|
|
||||||
|
// 예상 암호문 길이 계산
|
||||||
|
int expectedLen = getExpectedCipherLength(len, true);
|
||||||
|
|
||||||
|
// 복호화
|
||||||
|
String decryptedData = wrapper.decrypt("userId", "tableName", "columnName", encryptedData);
|
||||||
|
assertEquals(originalData, decryptedData, "Decrypted data does not match original at plain length=" + len);
|
||||||
|
|
||||||
|
// 로그 출력
|
||||||
|
log(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(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 암호문 예상 길이 계산 (AES/SEED/ARIA CBC 기준, PKCS5Padding)
|
||||||
|
*/
|
||||||
|
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 대체) */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(String message) {
|
||||||
|
System.out.println(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user