- 실패 처리 정책 개선: encrypt/hash 실패 시 예외 처리 추가
- decrypt 실패 시 입력 반환 및 경고 출력 방식 도입 - 실패 처리 관련 README 및 szRevision 최신화
This commit is contained in:
@@ -81,7 +81,7 @@ java -jar damo-manager.jar
|
|||||||
damo> enc hello
|
damo> enc hello
|
||||||
aGVsbG8=
|
aGVsbG8=
|
||||||
damo> info
|
damo> info
|
||||||
mode=FAKE revision=20260604 returnCode=0
|
mode=FAKE revision=20260805 returnCode=0
|
||||||
damo> quit
|
damo> quit
|
||||||
|
|
||||||
# 파라미터식: 1회 실행
|
# 파라미터식: 1회 실행
|
||||||
@@ -101,6 +101,11 @@ java -Ddamo-manager.enabled=true -jar damo-manager.jar enc "hello" # scpdb 있
|
|||||||
|
|
||||||
# 운영: real 강제 (scpdb 없으면 기동 실패)
|
# 운영: real 강제 (scpdb 없으면 기동 실패)
|
||||||
java -Ddamo-manager.require-real=true -jar damo-manager.jar info
|
java -Ddamo-manager.require-real=true -jar damo-manager.jar info
|
||||||
|
|
||||||
|
# decrypt 실패 폴백: 암호문이 아닌 입력은 그대로 에코되고 stderr 에 경고(마스킹) 출력
|
||||||
|
java -Ddamo-manager.enabled=true -jar damo-manager.jar dec 'not-real-cipher=='
|
||||||
|
# stdout: not-real-cipher==
|
||||||
|
# stderr: [damo-manager] WARN decrypt failed in fake mode; returning input as-is ...
|
||||||
```
|
```
|
||||||
|
|
||||||
명령: `enc|encrypt`, `dec|decrypt`, `sha256|hash256`, `sha512|hash512`, `info|mode`, `help|?`, (대화식) `quit|exit`. 알 수 없는 명령은 stderr 안내 후 exit code 2.
|
명령: `enc|encrypt`, `dec|decrypt`, `sha256|hash256`, `sha512|hash512`, `info|mode`, `help|?`, (대화식) `quit|exit`. 알 수 없는 명령은 stderr 안내 후 exit code 2.
|
||||||
@@ -240,6 +245,11 @@ int rc = damo2.getReturnCode(); // 0: 성공, -1:
|
|||||||
| `String decrypt(String b64)` | `ScpDecB64(INI_FILE, "KEY1", b64, "UTF-8")` | `new String(Base64.decode(b64), "UTF-8")` | 입력 그대로 |
|
| `String decrypt(String b64)` | `ScpDecB64(INI_FILE, "KEY1", b64, "UTF-8")` | `new String(Base64.decode(b64), "UTF-8")` | 입력 그대로 |
|
||||||
| `String hash(int type, String src)` | `ScpHashB64(INI_FILE, type, src)` | `MessageDigest(SHA-256/512).digest(src) → Base64` | `src` (원문) |
|
| `String hash(int type, String src)` | `ScpHashB64(INI_FILE, type, src)` | `MessageDigest(SHA-256/512).digest(src) → Base64` | `src` (원문) |
|
||||||
|
|
||||||
|
**실패 처리 정책** (bypass 는 실패 경로 없음):
|
||||||
|
|
||||||
|
- `decrypt` 실패(real 예외·빈 결과, fake Base64 디코딩 실패): **입력을 그대로 반환**하고 `returnCode=-1`, System.err 에 경고(입력은 앞 3자+길이만 마스킹 출력). 실패 원인은 대부분 "입력이 암호문이 아님"(레거시 평문이 Base64 형식으로 오판)이므로 원문을 보존해야 호출 측 평문 폴백/재암호화가 동작한다. 빈 문자열 반환은 오판된 평문을 빈 값으로 만들어 데이터 파괴로 이어진다.
|
||||||
|
- `encrypt`/`hash` 실패(real 예외·빈 결과): **`IllegalStateException` throw.** 암호화/해시 실패는 시스템 장애(ini/키/에이전트)이며, 빈 문자열을 조용히 반환하면 빈 비밀번호/빈 암호문이 DB에 저장되므로 저장 자체를 실패시킨다.
|
||||||
|
|
||||||
해시 `type` 상수:
|
해시 `type` 상수:
|
||||||
|
|
||||||
| 상수 | 값 | 알고리즘 |
|
| 상수 | 값 | 알고리즘 |
|
||||||
@@ -282,7 +292,7 @@ gradle runFakeSmoke # -Ddamo-manager.enabled=true (fake)
|
|||||||
...
|
...
|
||||||
============================================================
|
============================================================
|
||||||
|
|
||||||
revision = 20260604
|
revision = 20260805
|
||||||
bypassMode = false
|
bypassMode = false
|
||||||
fakeMode = true
|
fakeMode = true
|
||||||
encrypt = aGVsbG8sIOuLpOuqqA==
|
encrypt = aGVsbG8sIOuLpOuqqA==
|
||||||
|
|||||||
@@ -35,6 +35,16 @@ import java.util.Base64;
|
|||||||
* - encrypt(s) : Base64.encode(s.getBytes(UTF-8)) -- 암호화 없음
|
* - encrypt(s) : Base64.encode(s.getBytes(UTF-8)) -- 암호화 없음
|
||||||
* - decrypt(s) : new String(Base64.decode(s), UTF-8)
|
* - decrypt(s) : new String(Base64.decode(s), UTF-8)
|
||||||
* - hash(t, s) : SHA-256(71) / SHA-512(73) JRE MessageDigest -> Base64
|
* - hash(t, s) : SHA-256(71) / SHA-512(73) JRE MessageDigest -> Base64
|
||||||
|
*
|
||||||
|
* 실패 처리 정책 (real/fake 공통, bypass 는 실패 경로 없음):
|
||||||
|
* - decrypt 실패 (real 예외·빈 결과, fake Base64 디코딩 실패) :
|
||||||
|
* 입력을 그대로 반환하고 returnCode=-1, System.err 경고(입력은 앞 3자+길이만 마스킹 출력).
|
||||||
|
* 실패 원인은 대부분 "입력이 암호문이 아님"(레거시 평문이 Base64 형식으로 오판된 경우)이므로
|
||||||
|
* 원문을 보존해야 컨버터/마이그레이션의 평문 폴백이 동작한다. 빈 문자열을 반환하면 오판된
|
||||||
|
* 평문이 빈 값으로 읽히고 일괄 마이그레이션이 빈 값으로 UPDATE 하는 데이터 파괴로 이어진다.
|
||||||
|
* - encrypt/hash 실패 (real 예외·빈 결과) : IllegalStateException 을 던진다.
|
||||||
|
* 암호화/해시 실패는 시스템 장애(ini/키/에이전트)이며, 빈 문자열을 조용히 반환하면
|
||||||
|
* 빈 비밀번호/빈 암호문이 DB에 저장되는 무결성 파괴로 이어지므로 저장 자체를 실패시킨다.
|
||||||
*/
|
*/
|
||||||
public class DamoManager {
|
public class DamoManager {
|
||||||
|
|
||||||
@@ -141,7 +151,7 @@ public class DamoManager {
|
|||||||
System.err.flush();
|
System.err.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String szRevision = "20260604";
|
private final String szRevision = "20260805";
|
||||||
private int nReturn;
|
private int nReturn;
|
||||||
private final RealBridge bridge;
|
private final RealBridge bridge;
|
||||||
|
|
||||||
@@ -195,6 +205,7 @@ public class DamoManager {
|
|||||||
/**
|
/**
|
||||||
* 평문 문자열을 ScpEncB64 로 암호화하여 Base64 cipher text 반환.
|
* 평문 문자열을 ScpEncB64 로 암호화하여 Base64 cipher text 반환.
|
||||||
* fake 모드: 단순 Base64 인코딩.
|
* fake 모드: 단순 Base64 인코딩.
|
||||||
|
* real 실패(예외·빈 결과) 시 IllegalStateException — 빈 암호문이 DB에 저장되는 것을 막는다.
|
||||||
*/
|
*/
|
||||||
public String encrypt(String plain) {
|
public String encrypt(String plain) {
|
||||||
if (plain == null) {
|
if (plain == null) {
|
||||||
@@ -208,12 +219,19 @@ public class DamoManager {
|
|||||||
if (REAL_MODE) {
|
if (REAL_MODE) {
|
||||||
try {
|
try {
|
||||||
String out = this.bridge.encB64(plain);
|
String out = this.bridge.encB64(plain);
|
||||||
|
if (out == null || out.isEmpty()) {
|
||||||
|
this.nReturn = SCP_FAIL;
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"[damo-manager] encrypt failed in real mode: ScpEncB64 returned empty result");
|
||||||
|
}
|
||||||
this.nReturn = SCP_OK;
|
this.nReturn = SCP_OK;
|
||||||
return out;
|
return out;
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw e;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
|
||||||
this.nReturn = SCP_FAIL;
|
this.nReturn = SCP_FAIL;
|
||||||
return "";
|
throw new IllegalStateException(
|
||||||
|
"[damo-manager] encrypt failed in real mode: " + t.getClass().getName(), t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -228,6 +246,8 @@ public class DamoManager {
|
|||||||
/**
|
/**
|
||||||
* Base64 cipher text 를 ScpDecB64 로 복호화하여 평문 반환.
|
* Base64 cipher text 를 ScpDecB64 로 복호화하여 평문 반환.
|
||||||
* fake 모드: 단순 Base64 디코딩.
|
* fake 모드: 단순 Base64 디코딩.
|
||||||
|
* 실패(real 예외·빈 결과, fake 디코딩 실패) 시 입력을 그대로 반환하고 returnCode=-1.
|
||||||
|
* 레거시 평문이 암호문으로 오판된 경우 원문이 보존되어야 평문 폴백/재암호화가 동작한다.
|
||||||
*/
|
*/
|
||||||
public String decrypt(String base64Cipher) {
|
public String decrypt(String base64Cipher) {
|
||||||
if (base64Cipher == null) {
|
if (base64Cipher == null) {
|
||||||
@@ -241,12 +261,18 @@ public class DamoManager {
|
|||||||
if (REAL_MODE) {
|
if (REAL_MODE) {
|
||||||
try {
|
try {
|
||||||
String out = this.bridge.decB64(base64Cipher);
|
String out = this.bridge.decB64(base64Cipher);
|
||||||
|
if (out == null || out.isEmpty()) {
|
||||||
|
// ScpDecB64 가 예외 없이 실패(빈 결과)하는 경우도 입력 원문 반환으로 방어
|
||||||
|
this.nReturn = SCP_FAIL;
|
||||||
|
warnDecryptFallback("real", base64Cipher, null);
|
||||||
|
return base64Cipher;
|
||||||
|
}
|
||||||
this.nReturn = SCP_OK;
|
this.nReturn = SCP_OK;
|
||||||
return out;
|
return out;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
|
||||||
this.nReturn = SCP_FAIL;
|
this.nReturn = SCP_FAIL;
|
||||||
return "";
|
warnDecryptFallback("real", base64Cipher, t);
|
||||||
|
return base64Cipher; // 실패 시 입력 원문 반환 (레거시 평문 오판 가능성)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -259,13 +285,31 @@ public class DamoManager {
|
|||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
this.nReturn = SCP_FAIL;
|
this.nReturn = SCP_FAIL;
|
||||||
return "";
|
warnDecryptFallback("fake", base64Cipher, e);
|
||||||
|
return base64Cipher; // fake 도 동일: 실패 시 입력 원문 반환
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 복호화 실패 경고. 입력이 레거시 평문(개인정보)일 수 있으므로
|
||||||
|
* 원문 전체/예외 메시지를 찍지 않고 앞 3자 + 길이, 예외 클래스명만 출력한다.
|
||||||
|
*/
|
||||||
|
private static void warnDecryptFallback(String mode, String input, Throwable cause) {
|
||||||
|
System.err.println("[damo-manager] WARN decrypt failed in " + mode
|
||||||
|
+ " mode; returning input as-is (returnCode=-1). input=" + maskForLog(input)
|
||||||
|
+ " cause=" + (cause == null ? "empty-result" : cause.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String maskForLog(String s) {
|
||||||
|
int len = s.length();
|
||||||
|
String head = (len <= 3) ? "***" : s.substring(0, 3);
|
||||||
|
return head + "...(len=" + len + ")";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 단방향 해시. type 71 = SHA-256, 73 = SHA-512. 결과는 Base64 문자열.
|
* 단방향 해시. type 71 = SHA-256, 73 = SHA-512. 결과는 Base64 문자열.
|
||||||
* fake 모드: JRE MessageDigest 사용 (실제 SHA-256/512 결과를 Base64 로 반환).
|
* fake 모드: JRE MessageDigest 사용 (실제 SHA-256/512 결과를 Base64 로 반환).
|
||||||
|
* real 실패(예외·빈 결과) 시 IllegalStateException — 빈 해시(비밀번호 등)가 DB에 저장되는 것을 막는다.
|
||||||
*/
|
*/
|
||||||
public String hash(int type, String src) {
|
public String hash(int type, String src) {
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
@@ -279,12 +323,19 @@ public class DamoManager {
|
|||||||
if (REAL_MODE) {
|
if (REAL_MODE) {
|
||||||
try {
|
try {
|
||||||
String out = this.bridge.hashB64(type, src);
|
String out = this.bridge.hashB64(type, src);
|
||||||
|
if (out == null || out.isEmpty()) {
|
||||||
|
this.nReturn = SCP_FAIL;
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"[damo-manager] hash failed in real mode: ScpHashB64 returned empty result (type=" + type + ")");
|
||||||
|
}
|
||||||
this.nReturn = SCP_OK;
|
this.nReturn = SCP_OK;
|
||||||
return out;
|
return out;
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw e;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
|
||||||
this.nReturn = SCP_FAIL;
|
this.nReturn = SCP_FAIL;
|
||||||
return "";
|
throw new IllegalStateException(
|
||||||
|
"[damo-manager] hash failed in real mode (type=" + type + "): " + t.getClass().getName(), t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String algo;
|
String algo;
|
||||||
|
|||||||
Reference in New Issue
Block a user