198 lines
5.3 KiB
TypeScript
198 lines
5.3 KiB
TypeScript
// 기존 Java EncryptUtil 클래스를 Node.js로 포팅
|
|
// AES/ECB/NoPadding 암호화 방식 사용
|
|
|
|
import crypto from "crypto";
|
|
|
|
// 기존 Java Constants에서 가져온 값들
|
|
const KEY_NAME = "ILJIAESSECRETKEY";
|
|
const ALGORITHM = "AES";
|
|
const MASTER_PWD = "qlalfqjsgh11";
|
|
|
|
export class PasswordUtils {
|
|
/**
|
|
* 기존 Java EncryptUtil.encrypt() 메서드 포팅
|
|
* AES/ECB/NoPadding 방식으로 암호화
|
|
*/
|
|
static encrypt(source: string): string {
|
|
try {
|
|
// 16바이트 키 생성 (AES-128)
|
|
const key = Buffer.from(KEY_NAME, "utf8").slice(0, 16);
|
|
|
|
// 패딩 추가 (16바이트 블록 크기에 맞춤)
|
|
const paddedData = this.addPadding(Buffer.from(source, "utf8"));
|
|
|
|
// AES 암호화
|
|
const cipher = crypto.createCipher("aes-128-ecb", key);
|
|
cipher.setAutoPadding(false); // NoPadding 모드
|
|
|
|
let encrypted = cipher.update(paddedData);
|
|
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
|
|
|
// 16진수 문자열로 변환
|
|
return this.fromHex(encrypted);
|
|
} catch (error) {
|
|
console.error("Password encryption error:", error);
|
|
throw new Error("암호화 중 오류가 발생했습니다.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 기존 Java EncryptUtil.decrypt() 메서드 포팅
|
|
*/
|
|
static decrypt(source: string): string {
|
|
try {
|
|
// 16바이트 키 생성
|
|
const key = Buffer.from(KEY_NAME, "utf8").slice(0, 16);
|
|
|
|
// 16진수 문자열을 바이트 배열로 변환
|
|
const encryptedData = this.toBytes(source);
|
|
|
|
// AES 복호화
|
|
const decipher = crypto.createDecipher("aes-128-ecb", key);
|
|
decipher.setAutoPadding(false); // NoPadding 모드
|
|
|
|
let decrypted = decipher.update(encryptedData);
|
|
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
|
|
// 패딩 제거
|
|
const unpaddedData = this.removePadding(decrypted);
|
|
|
|
return unpaddedData.toString("utf8");
|
|
} catch (error) {
|
|
console.error("Password decryption error:", error);
|
|
throw new Error("복호화 중 오류가 발생했습니다.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 기존 Java EncryptUtil.encryptSha256() 메서드 포팅
|
|
*/
|
|
static encryptSha256(s: string): string {
|
|
try {
|
|
const hash = crypto.createHash("sha256");
|
|
hash.update(s, "utf8");
|
|
return hash.digest("hex");
|
|
} catch (error) {
|
|
console.error("SHA256 encryption error:", error);
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 비밀번호 검증 (기존 Java 로직과 동일)
|
|
*/
|
|
static matches(plainPassword: string, encryptedPassword: string): boolean {
|
|
try {
|
|
// 마스터 패스워드 체크
|
|
if (MASTER_PWD === plainPassword) {
|
|
return true;
|
|
}
|
|
|
|
// 일반 패스워드 암호화 후 비교
|
|
const encryptedPlainPassword = this.encrypt(plainPassword);
|
|
return encryptedPlainPassword === encryptedPassword;
|
|
} catch (error) {
|
|
console.error("Password matching error:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 기존 Java addPadding() 메서드 포팅
|
|
* 16바이트 블록 크기에 맞춰 패딩 추가
|
|
*/
|
|
private static addPadding(pBytes: Buffer): Buffer {
|
|
const pCount = pBytes.length;
|
|
const tCount = pCount + (16 - (pCount % 16));
|
|
const tBytes = Buffer.alloc(tCount);
|
|
|
|
pBytes.copy(tBytes, 0);
|
|
|
|
// 나머지 바이트를 0x00으로 채움
|
|
for (let rIndex = pCount; rIndex < tCount; rIndex++) {
|
|
tBytes[rIndex] = 0x00;
|
|
}
|
|
|
|
return tBytes;
|
|
}
|
|
|
|
/**
|
|
* 기존 Java removePadding() 메서드 포팅
|
|
* 패딩 제거
|
|
*/
|
|
private static removePadding(pBytes: Buffer): Buffer {
|
|
const pCount = pBytes.length;
|
|
let index = 0;
|
|
let loop = true;
|
|
|
|
while (loop) {
|
|
if (index === pCount || pBytes[index] === 0x00) {
|
|
loop = false;
|
|
index--;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
const tBytes = Buffer.alloc(index);
|
|
pBytes.copy(tBytes, 0, 0, index);
|
|
|
|
return tBytes;
|
|
}
|
|
|
|
/**
|
|
* 기존 Java toBytes() 메서드 포팅
|
|
* 16진수 문자열을 바이트 배열로 변환
|
|
*/
|
|
private static toBytes(pSource: string): Buffer {
|
|
const buff = pSource;
|
|
const bCount = Math.floor(buff.length / 2);
|
|
const bArr = Buffer.alloc(bCount);
|
|
|
|
for (let bIndex = 0; bIndex < bCount; bIndex++) {
|
|
const hexByte = buff.substring(2 * bIndex, 2 * bIndex + 2);
|
|
bArr[bIndex] = parseInt(hexByte, 16);
|
|
}
|
|
|
|
return bArr;
|
|
}
|
|
|
|
/**
|
|
* 기존 Java fromHex() 메서드 포팅
|
|
* 바이트 배열을 16진수 문자열로 변환
|
|
*/
|
|
private static fromHex(pBytes: Buffer): string {
|
|
const pCount = pBytes.length;
|
|
let buff = "";
|
|
|
|
for (let pIndex = 0; pIndex < pCount; pIndex++) {
|
|
const byte = pBytes[pIndex] & 0xff;
|
|
if (byte < 0x10) {
|
|
buff += "0";
|
|
}
|
|
buff += byte.toString(16);
|
|
}
|
|
|
|
return buff;
|
|
}
|
|
|
|
/**
|
|
* 테스트용 메서드 (개발 환경에서만 사용)
|
|
*/
|
|
static testEncryption(plainText: string): void {
|
|
console.log("=== 암호화 테스트 ===");
|
|
console.log("원본 텍스트:", plainText);
|
|
|
|
const encrypted = this.encrypt(plainText);
|
|
console.log("암호화 결과:", encrypted);
|
|
|
|
const decrypted = this.decrypt(encrypted);
|
|
console.log("복호화 결과:", decrypted);
|
|
|
|
const isMatch = this.matches(plainText, encrypted);
|
|
console.log("비밀번호 일치:", isMatch);
|
|
|
|
const sha256Hash = this.encryptSha256(plainText);
|
|
console.log("SHA256 해시:", sha256Hash);
|
|
}
|
|
}
|