62 lines
1.5 KiB
TypeScript
62 lines
1.5 KiB
TypeScript
|
|
import crypto from "crypto";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 자격 증명 암호화 유틸리티
|
||
|
|
* AES-256-GCM 알고리즘 사용
|
||
|
|
*/
|
||
|
|
export class CredentialEncryption {
|
||
|
|
private algorithm = "aes-256-gcm";
|
||
|
|
private key: Buffer;
|
||
|
|
|
||
|
|
constructor(secretKey: string) {
|
||
|
|
// scrypt로 안전한 키 생성
|
||
|
|
this.key = crypto.scryptSync(secretKey, "salt", 32);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 평문을 암호화
|
||
|
|
*/
|
||
|
|
encrypt(text: string): string {
|
||
|
|
const iv = crypto.randomBytes(16);
|
||
|
|
const cipher = crypto.createCipheriv(
|
||
|
|
this.algorithm,
|
||
|
|
this.key,
|
||
|
|
iv
|
||
|
|
) as crypto.CipherGCM;
|
||
|
|
|
||
|
|
let encrypted = cipher.update(text, "utf8", "hex");
|
||
|
|
encrypted += cipher.final("hex");
|
||
|
|
|
||
|
|
const authTag = cipher.getAuthTag();
|
||
|
|
|
||
|
|
// IV:AuthTag:EncryptedText 형식으로 반환
|
||
|
|
return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 암호문을 복호화
|
||
|
|
*/
|
||
|
|
decrypt(encrypted: string): string {
|
||
|
|
const [ivHex, authTagHex, encryptedText] = encrypted.split(":");
|
||
|
|
|
||
|
|
if (!ivHex || !authTagHex || !encryptedText) {
|
||
|
|
throw new Error("Invalid encrypted string format");
|
||
|
|
}
|
||
|
|
|
||
|
|
const iv = Buffer.from(ivHex, "hex");
|
||
|
|
const authTag = Buffer.from(authTagHex, "hex");
|
||
|
|
const decipher = crypto.createDecipheriv(
|
||
|
|
this.algorithm,
|
||
|
|
this.key,
|
||
|
|
iv
|
||
|
|
) as crypto.DecipherGCM;
|
||
|
|
|
||
|
|
decipher.setAuthTag(authTag);
|
||
|
|
|
||
|
|
let decrypted = decipher.update(encryptedText, "hex", "utf8");
|
||
|
|
decrypted += decipher.final("utf8");
|
||
|
|
|
||
|
|
return decrypted;
|
||
|
|
}
|
||
|
|
}
|