/** * DOLCE 파일 다운로드용 DES 암호화 유틸리티 * C# DESCryptoServiceProvider와 호환되는 Node.js 구현 * * OpenSSL 3.0 호환 처리: * - Node.js 17+ 환경에서 DES는 레거시 알고리즘으로 분류됨 * - 기본적으로 crypto-js 기반 구현 사용 (OpenSSL 독립적) * - 환경 변수로 Native crypto 사용 가능: USE_NATIVE_CRYPTO=true */ import crypto from "crypto"; // 암호화 키 (8바이트) const DES_KEY = Buffer.from("4fkkdijg", "ascii"); // OpenSSL Native Crypto 사용 여부 (기본: false, crypto-js 사용) const USE_NATIVE_CRYPTO = process.env.USE_NATIVE_CRYPTO === "true"; // OpenSSL 3.0 환경 감지 let useModernCrypto = USE_NATIVE_CRYPTO; /** * DES ECB 모드로 문자열 암호화 * @param plainText 암호화할 평문 * @returns Base64 인코딩된 암호문 */ export function encryptDES(plainText: string): string { // 기본적으로 레거시 구현(crypto-js) 사용 if (!useModernCrypto) { return encryptDESLegacy(plainText); } // Native crypto 시도 (USE_NATIVE_CRYPTO=true인 경우만) try { const cipher = crypto.createCipheriv("des-ecb", DES_KEY, Buffer.alloc(0)); cipher.setAutoPadding(true); let encrypted = cipher.update(plainText, "utf8", "base64"); encrypted += cipher.final("base64"); return encrypted.replace(/\+/g, "|||"); } catch (error: any) { // OpenSSL 에러 감지 - 레거시로 폴백 if ( error.code === "ERR_OSSL_EVP_UNSUPPORTED" || error.message?.includes("unsupported") || error.message?.includes("digital envelope") ) { console.warn("[DOLCE] OpenSSL DES 미지원, crypto-js로 전환합니다."); useModernCrypto = false; return encryptDESLegacy(plainText); } throw error; } } /** * 레거시 DES 암호화 (crypto-js 기반, OpenSSL 독립적) */ function encryptDESLegacy(plainText: string): string { try { const { encryptDES: legacyEncrypt } = require("./crypto-utils-legacy"); return legacyEncrypt(plainText); } catch (error) { console.error("[DOLCE] DES 암호화 실패:", error); throw new Error(`암호화 중 오류가 발생했습니다: ${error}`); } } /** * DES ECB 모드로 문자열 복호화 * @param encryptedText Base64 인코딩된 암호문 * @returns 복호화된 평문 */ export function decryptDES(encryptedText: string): string { // 기본적으로 레거시 구현(crypto-js) 사용 if (!useModernCrypto) { return decryptDESLegacy(encryptedText); } // Native crypto 시도 (USE_NATIVE_CRYPTO=true인 경우만) try { const restored = encryptedText.replace(/\|\|\|/g, "+"); const decipher = crypto.createDecipheriv("des-ecb", DES_KEY, Buffer.alloc(0)); decipher.setAutoPadding(true); let decrypted = decipher.update(restored, "base64", "utf8"); decrypted += decipher.final("utf8"); return decrypted.replace(/\0+$/, ""); } catch (error: any) { // OpenSSL 에러 감지 - 레거시로 폴백 if ( error.code === "ERR_OSSL_EVP_UNSUPPORTED" || error.message?.includes("unsupported") || error.message?.includes("digital envelope") ) { console.warn("[DOLCE] OpenSSL DES 미지원, crypto-js로 전환합니다."); useModernCrypto = false; return decryptDESLegacy(encryptedText); } throw error; } } /** * 레거시 DES 복호화 (crypto-js 기반, OpenSSL 독립적) */ function decryptDESLegacy(encryptedText: string): string { try { const { decryptDES: legacyDecrypt } = require("./crypto-utils-legacy"); return legacyDecrypt(encryptedText); } catch (error) { console.error("[DOLCE] DES 복호화 실패:", error); throw new Error(`복호화 중 오류가 발생했습니다: ${error}`); } } /** * DOLCE 파일 다운로드용 암호화 키 생성 * @param fileId 파일 ID * @param userId 사용자 ID * @param fileName 파일명 * @returns 암호화된 키 */ export function createDolceDownloadKey( fileId: string, userId: string, fileName: string ): string { // FileId↔UserId↔FileName 형식으로 조합 const plainText = `${fileId}↔${userId}↔${fileName}`; // DES 암호화 return encryptDES(plainText); } /** * 테스트용: 암호화/복호화 검증 */ export function testDESEncryption(testString: string): { original: string; encrypted: string; decrypted: string; match: boolean; } { const encrypted = encryptDES(testString); const decrypted = decryptDES(encrypted); return { original: testString, encrypted, decrypted, match: testString === decrypted, }; }