- damo-manager CLI 추가: DamoCli.java / Main-Class 선언
- Jenkinsfile 도입: 자동 빌드/테스트 워크플로 작성 - bypass 모드 도입: 시스템 프로퍼티로 암복호화 설정 분기
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package com.eactive.ext.djb;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* damo-manager.jar 의 CLI 진입점 (manifest Main-Class).
|
||||
* encrypt/decrypt/hash 동작을 운영/로컬에서 빠르게 확인하는 테스트 도구.
|
||||
*
|
||||
* 실행 방식:
|
||||
* java -jar damo-manager.jar # 대화식(REPL)
|
||||
* java -jar damo-manager.jar <cmd> [text] # 파라미터식 1회 실행
|
||||
* echo "<text>" | java -jar damo-manager.jar <cmd> # stdin 입력 → stdout 출력
|
||||
*
|
||||
* 명령:
|
||||
* enc|encrypt <text> 암호화 (real=ScpEncB64, fake=Base64, bypass=원문)
|
||||
* dec|decrypt <text> 복호화
|
||||
* sha256|hash256 <text> SHA-256 해시 (bypass=원문)
|
||||
* sha512|hash512 <text> SHA-512 해시 (bypass=원문)
|
||||
* info|mode 현재 모드/리비전/리턴코드
|
||||
* help|? 도움말
|
||||
* quit|exit (대화식) 종료
|
||||
*
|
||||
* 기본은 bypass(무변환). 실제 암복호화(real/fake) 를 쓰려면 -Ddamo-manager.enabled=true
|
||||
*
|
||||
* stdin/stdout: 파라미터식에서 text 인자를 생략하면 stdin 전체를 읽어 입력으로 사용하고
|
||||
* 결과를 stdout 으로 출력하므로 파이프 연동이 가능하다 (후행 개행 1개는 제거).
|
||||
*/
|
||||
public final class DamoCli {
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
private DamoCli() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
PrintStream out = new PrintStream(System.out, true, "UTF-8");
|
||||
PrintStream err = new PrintStream(System.err, true, "UTF-8");
|
||||
DamoManager damo = new DamoManager();
|
||||
|
||||
if (args.length == 0) {
|
||||
repl(damo, out, err);
|
||||
return;
|
||||
}
|
||||
|
||||
String cmd = args[0];
|
||||
String lower = cmd.toLowerCase();
|
||||
|
||||
if (lower.equals("info") || lower.equals("mode")) {
|
||||
out.println(modeLine(damo));
|
||||
return;
|
||||
}
|
||||
if (lower.equals("help") || lower.equals("?") || lower.equals("-h") || lower.equals("--help")) {
|
||||
out.println(usage());
|
||||
return;
|
||||
}
|
||||
|
||||
String text;
|
||||
if (args.length >= 2) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
if (i > 1) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(args[i]);
|
||||
}
|
||||
text = sb.toString();
|
||||
} else {
|
||||
if (System.console() != null) {
|
||||
err.println("(stdin 입력 대기중... 입력 후 Ctrl-D)");
|
||||
}
|
||||
text = readAllStdin();
|
||||
}
|
||||
|
||||
String result = run(damo, cmd, text, err);
|
||||
if (result == null) {
|
||||
System.exit(2);
|
||||
}
|
||||
out.println(result);
|
||||
}
|
||||
|
||||
private static void repl(DamoManager damo, PrintStream out, PrintStream err) throws IOException {
|
||||
boolean tty = System.console() != null;
|
||||
if (tty) {
|
||||
out.println("damo-manager CLI (" + modeLine(damo) + ")");
|
||||
out.println("명령: enc <t> | dec <t> | sha256 <t> | sha512 <t> | info | help | quit");
|
||||
}
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(System.in, UTF8));
|
||||
String line;
|
||||
while (true) {
|
||||
if (tty) {
|
||||
out.print("damo> ");
|
||||
out.flush();
|
||||
}
|
||||
line = in.readLine();
|
||||
if (line == null) {
|
||||
break; // EOF
|
||||
}
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int sp = line.indexOf(' ');
|
||||
String cmd = sp < 0 ? line : line.substring(0, sp);
|
||||
String text = sp < 0 ? "" : line.substring(sp + 1);
|
||||
String lower = cmd.toLowerCase();
|
||||
|
||||
if (lower.equals("quit") || lower.equals("exit") || lower.equals("q")) {
|
||||
break;
|
||||
}
|
||||
if (lower.equals("info") || lower.equals("mode")) {
|
||||
out.println(modeLine(damo));
|
||||
continue;
|
||||
}
|
||||
if (lower.equals("help") || lower.equals("?")) {
|
||||
out.println(usage());
|
||||
continue;
|
||||
}
|
||||
|
||||
String result = run(damo, cmd, text, err);
|
||||
if (result != null) {
|
||||
out.println(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 명령 실행. 알 수 없는 명령이면 stderr 에 안내 후 null 반환. */
|
||||
private static String run(DamoManager damo, String cmd, String text, PrintStream err) {
|
||||
String lower = cmd.toLowerCase();
|
||||
if (lower.equals("enc") || lower.equals("encrypt")) {
|
||||
return damo.encrypt(text);
|
||||
}
|
||||
if (lower.equals("dec") || lower.equals("decrypt")) {
|
||||
return damo.decrypt(text);
|
||||
}
|
||||
if (lower.equals("sha256") || lower.equals("hash256")) {
|
||||
return damo.hash(DamoManager.SHA256, text);
|
||||
}
|
||||
if (lower.equals("sha512") || lower.equals("hash512")) {
|
||||
return damo.hash(DamoManager.SHA512, text);
|
||||
}
|
||||
err.println("unknown command: " + cmd);
|
||||
err.println(usage());
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String modeLine(DamoManager damo) {
|
||||
String mode = damo.isBypassMode() ? "BYPASS" : (damo.isFakeMode() ? "FAKE" : "REAL");
|
||||
return "mode=" + mode
|
||||
+ " revision=" + damo.getRevision()
|
||||
+ " returnCode=" + damo.getReturnCode();
|
||||
}
|
||||
|
||||
private static String usage() {
|
||||
return String.join("\n",
|
||||
"Usage:",
|
||||
" java -jar damo-manager.jar # 대화식(REPL)",
|
||||
" java -jar damo-manager.jar <cmd> [text] # 파라미터식 1회 실행",
|
||||
" echo \"<text>\" | java -jar damo-manager.jar <cmd> # stdin 입력",
|
||||
"",
|
||||
"Commands:",
|
||||
" enc|encrypt <text> 암호화 (real=ScpEncB64, fake=Base64, bypass=원문)",
|
||||
" dec|decrypt <text> 복호화",
|
||||
" sha256|hash256 <text> SHA-256 해시 (bypass=원문)",
|
||||
" sha512|hash512 <text> SHA-512 해시 (bypass=원문)",
|
||||
" info|mode 현재 모드/리비전/리턴코드",
|
||||
" help|? 도움말",
|
||||
" quit|exit 대화식 종료",
|
||||
"",
|
||||
"모드 옵션 (기본은 bypass 무변환):",
|
||||
" -Ddamo-manager.enabled=true real/fake 동작 활성화 (scpdb 있으면 real, 없으면 fake)",
|
||||
" -Ddamo-manager.require-real=true 운영: real 강제. real 불가 시 기동 실패(fake/bypass 폴백 금지)");
|
||||
}
|
||||
|
||||
private static String readAllStdin() throws IOException {
|
||||
InputStream in = System.in;
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
byte[] buf = new byte[4096];
|
||||
int n;
|
||||
while ((n = in.read(buf)) != -1) {
|
||||
bos.write(buf, 0, n);
|
||||
}
|
||||
String s = new String(bos.toByteArray(), UTF8);
|
||||
// echo 등으로 들어온 후행 개행 1개 제거
|
||||
if (s.endsWith("\r\n")) {
|
||||
return s.substring(0, s.length() - 2);
|
||||
}
|
||||
if (s.endsWith("\n")) {
|
||||
return s.substring(0, s.length() - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,29 @@ import java.util.Base64;
|
||||
/**
|
||||
* 운영(penta scpdb 라이브러리 존재) / 로컬(scpdb 부재) 양쪽에서 동작하는 단일 클래스.
|
||||
*
|
||||
* 클래스패스에 {@code com.penta.scpdb.ScpDbAgent} 가 존재하면 실 호출, 아니면 fake 모드로 자동 fallback.
|
||||
* fake 모드 진입 시 클래스 로딩 시점에 System.err 로 강한 경고 배너 출력.
|
||||
* 동작 모드는 다음 규칙으로 한 번만 결정된다 (클래스 로딩 시점).
|
||||
* - 기본값(시스템 프로퍼티 미설정) : bypass. 암복호화/해시를 전혀 하지 않고 원문 그대로.
|
||||
* - {@code -Ddamo-manager.enabled=true} 설정 시 : 실제 동작(real/fake).
|
||||
* · real : 클래스패스에 {@code com.penta.scpdb.ScpDbAgent} 가 존재.
|
||||
* · fake : scpdb 부재 시 자동 fallback.
|
||||
* - {@code -Ddamo-manager.require-real=true} (운영 보호) : real 모드로만 동작.
|
||||
* enabled 를 함의하며, real 이 불가능하면 fake/bypass 로 폴백하지 않고 기동 시점에 즉시 실패.
|
||||
* bypass / fake 진입 시 클래스 로딩 시점에 System.err 로 강한 경고 배너 출력.
|
||||
*
|
||||
* fake 모드 동작:
|
||||
* 즉 아무 옵션 없이 기동하면 안전하게 무변환(bypass)으로 동작하고,
|
||||
* 실제 암복호화를 쓰려면 {@code -Ddamo-manager.enabled=true} 를 명시해야 한다.
|
||||
* 운영 서버에서 fake/bypass 로 잘못 동작하는 것을 막으려면 {@code -Ddamo-manager.require-real=true} 를 준다.
|
||||
*
|
||||
* bypass 모드 동작 (기본값, 암복호화/해시 전면 통과):
|
||||
* - encrypt(s) : s -- 인코딩조차 하지 않고 원문 그대로
|
||||
* - decrypt(s) : s -- 디코딩조차 하지 않고 원문 그대로
|
||||
* - hash(t, s) : s -- 해시하지 않고 원문 그대로
|
||||
* bypass 는 fake 와 다르다. fake 는 Base64/해시 변환을 하지만, bypass 는 아무 변환도 하지 않는다.
|
||||
*
|
||||
* real 모드 동작 (-Ddamo-manager.enabled=true + scpdb 존재):
|
||||
* - encrypt/decrypt/hash : 실제 ScpEncB64 / ScpDecB64 / ScpHashB64 호출
|
||||
*
|
||||
* fake 모드 동작 (-Ddamo-manager.enabled=true + scpdb 부재, 로컬 개발용):
|
||||
* - encrypt(s) : Base64.encode(s.getBytes(UTF-8)) -- 암호화 없음
|
||||
* - decrypt(s) : new String(Base64.decode(s), UTF-8)
|
||||
* - hash(t, s) : SHA-256(71) / SHA-512(73) JRE MessageDigest -> Base64
|
||||
@@ -29,19 +48,82 @@ public class DamoManager {
|
||||
private static final int SCP_OK = 0;
|
||||
private static final int SCP_FAIL = -1;
|
||||
|
||||
/**
|
||||
* 실제 동작(real/fake) 활성화 플래그. 이 시스템 프로퍼티가 {@code true} 일 때만 암복호화/해시를 수행한다.
|
||||
* 미설정(기본) 이면 bypass(무변환).
|
||||
* 예: {@code -Ddamo-manager.enabled=true}
|
||||
*/
|
||||
public static final String ENABLE_PROPERTY = "damo-manager.enabled";
|
||||
|
||||
/**
|
||||
* 운영 보호 플래그. {@code true} 이면 real 모드(penta scpdb 실 암복호화)로만 동작해야 하며,
|
||||
* scpdb 가 없어 fake/bypass 로 떨어질 상황이면 조용히 폴백하지 않고 클래스 로딩 시점에 즉시 실패한다.
|
||||
* 이 플래그는 {@link #ENABLE_PROPERTY} 를 함의하므로, 운영 서버에서는 이 플래그 하나만 켜면
|
||||
* "real 아니면 기동 거부" 가 된다.
|
||||
* 예: {@code -Ddamo-manager.require-real=true}
|
||||
*/
|
||||
public static final String REQUIRE_REAL_PROPERTY = "damo-manager.require-real";
|
||||
|
||||
/** 기본값(ENABLE_PROPERTY 미설정) 이면 bypass: 암복호화/해시를 전혀 하지 않고 원문을 그대로 사용. */
|
||||
private static final boolean BYPASS_MODE;
|
||||
private static final boolean REAL_MODE;
|
||||
|
||||
static {
|
||||
boolean requireReal = "true".equalsIgnoreCase(System.getProperty(REQUIRE_REAL_PROPERTY));
|
||||
// require-real 은 enable 을 함의한다 (운영에서 real 강제).
|
||||
boolean enabled = requireReal
|
||||
|| "true".equalsIgnoreCase(System.getProperty(ENABLE_PROPERTY));
|
||||
|
||||
boolean bypass;
|
||||
boolean real;
|
||||
try {
|
||||
Class.forName("com.penta.scpdb.ScpDbAgent", false,
|
||||
DamoManager.class.getClassLoader());
|
||||
real = true;
|
||||
} catch (Throwable t) {
|
||||
if (!enabled) {
|
||||
// 기본: 아무 옵션 없으면 무변환(bypass)
|
||||
bypass = true;
|
||||
real = false;
|
||||
} else {
|
||||
bypass = false;
|
||||
try {
|
||||
Class.forName("com.penta.scpdb.ScpDbAgent", false,
|
||||
DamoManager.class.getClassLoader());
|
||||
real = true;
|
||||
} catch (Throwable t) {
|
||||
real = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (requireReal && !real) {
|
||||
// 운영 보호: real 이 아니면(=fake/bypass) 조용히 동작하지 않고 기동 자체를 실패시킨다.
|
||||
throw new IllegalStateException(
|
||||
"[damo-manager] " + REQUIRE_REAL_PROPERTY + "=true 이지만 real 모드를 사용할 수 없습니다. "
|
||||
+ "com.penta.scpdb.ScpDbAgent 가 클래스패스에 없어 fake/bypass 로 동작하게 되므로 기동을 중단합니다. "
|
||||
+ "운영에서는 scpdb 라이브러리/네이티브/ini(" + INI_FILE + ") 를 갖춰 real 모드로만 동작해야 합니다.");
|
||||
}
|
||||
|
||||
BYPASS_MODE = bypass;
|
||||
REAL_MODE = real;
|
||||
|
||||
if (bypass) {
|
||||
printBypassBanner();
|
||||
} else if (!real) {
|
||||
printFakeBanner();
|
||||
}
|
||||
REAL_MODE = real;
|
||||
}
|
||||
|
||||
private static void printBypassBanner() {
|
||||
String line = "============================================================";
|
||||
System.err.println();
|
||||
System.err.println(line);
|
||||
System.err.println(" *** DAMO-MANAGER BYPASS MODE (default) ***");
|
||||
System.err.println(" 암복호화/해시를 전혀 수행하지 않고 원문을 그대로 사용합니다.");
|
||||
System.err.println(" Pass-through (NO transform at all):");
|
||||
System.err.println(" encrypt(s) -> s (NO ENCODING)");
|
||||
System.err.println(" decrypt(s) -> s (NO DECODING)");
|
||||
System.err.println(" hash(t,s) -> s (NO HASHING)");
|
||||
System.err.println(" 실제 암복호화(real/fake) 가 필요하면: -D" + ENABLE_PROPERTY + "=true");
|
||||
System.err.println(" *** DO NOT use this build for production! ***");
|
||||
System.err.println(line);
|
||||
System.err.println();
|
||||
System.err.flush();
|
||||
}
|
||||
|
||||
private static void printFakeBanner() {
|
||||
@@ -98,7 +180,12 @@ public class DamoManager {
|
||||
}
|
||||
|
||||
public boolean isFakeMode() {
|
||||
return !REAL_MODE;
|
||||
return !REAL_MODE && !BYPASS_MODE;
|
||||
}
|
||||
|
||||
/** 기본(ENABLE_PROPERTY 미설정) 으로 기동되어 암복호화/해시를 전혀 하지 않는 통과 모드인지 여부. */
|
||||
public boolean isBypassMode() {
|
||||
return BYPASS_MODE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -114,6 +201,10 @@ public class DamoManager {
|
||||
this.nReturn = SCP_FAIL;
|
||||
return "";
|
||||
}
|
||||
if (BYPASS_MODE) {
|
||||
this.nReturn = SCP_OK;
|
||||
return plain; // 원문 그대로. 인코딩조차 하지 않음.
|
||||
}
|
||||
if (REAL_MODE) {
|
||||
try {
|
||||
String out = this.bridge.encB64(plain);
|
||||
@@ -143,6 +234,10 @@ public class DamoManager {
|
||||
this.nReturn = SCP_FAIL;
|
||||
return "";
|
||||
}
|
||||
if (BYPASS_MODE) {
|
||||
this.nReturn = SCP_OK;
|
||||
return base64Cipher; // 원문 그대로. 디코딩조차 하지 않음.
|
||||
}
|
||||
if (REAL_MODE) {
|
||||
try {
|
||||
String out = this.bridge.decB64(base64Cipher);
|
||||
@@ -177,6 +272,10 @@ public class DamoManager {
|
||||
this.nReturn = SCP_FAIL;
|
||||
return "";
|
||||
}
|
||||
if (BYPASS_MODE) {
|
||||
this.nReturn = SCP_OK;
|
||||
return src; // 원문 그대로. 해시하지 않음.
|
||||
}
|
||||
if (REAL_MODE) {
|
||||
try {
|
||||
String out = this.bridge.hashB64(type, src);
|
||||
@@ -356,8 +455,9 @@ public class DamoManager {
|
||||
// -----------------------------------------------------------------
|
||||
public static void main(String[] args) {
|
||||
DamoManager m = new DamoManager();
|
||||
System.out.println("revision = " + m.getRevision());
|
||||
System.out.println("fakeMode = " + m.isFakeMode());
|
||||
System.out.println("revision = " + m.getRevision());
|
||||
System.out.println("bypassMode = " + m.isBypassMode());
|
||||
System.out.println("fakeMode = " + m.isFakeMode());
|
||||
|
||||
String plain = "hello, 다모";
|
||||
String enc = m.encrypt(plain);
|
||||
|
||||
Reference in New Issue
Block a user