const crypto = require('crypto'); const db = require('../db'); const ALGORITHM = 'aes-256-cbc'; const SYSTEM_INTERNAL_KEY = 'ims_system_l2_internal_protection_key_2026'; // 고정 시스템 키 (2단계 보안용) let cachedKey = null; const cryptoUtil = { /** * 내부 보안용 암호화 (마스터 키 보호용) */ _internalProcess(text, isEncrypt = true) { if (!text) return text; try { const keyBuffer = crypto.scryptSync(SYSTEM_INTERNAL_KEY, 'ims_salt', 32); if (isEncrypt) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(ALGORITHM, keyBuffer, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); return iv.toString('hex') + ':' + encrypted; } else { if (!text.includes(':')) return text; // 평문인 경우 그대로 반환 const [ivHex, cipherText] = text.split(':'); const iv = Buffer.from(ivHex, 'hex'); const decipher = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv); let decrypted = decipher.update(cipherText, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } } catch (e) { return text; } }, /** * Get key from DB or Cache */ async getMasterKey() { if (cachedKey) return cachedKey; try { const [rows] = await db.query("SELECT setting_value FROM system_settings WHERE setting_key = 'encryption_key'"); if (rows.length > 0 && rows[0].setting_value) { const rawValue = rows[0].setting_value; // DB에 저장된 값이 암호화된 형태(iv 포함)라면 복호화하여 사용 if (rawValue.includes(':')) { cachedKey = this._internalProcess(rawValue, false); } else { cachedKey = rawValue; } return cachedKey; } } catch (e) { console.error('CryptoUtil: Failed to fetch key', e); } return process.env.ENCRYPTION_KEY || 'smartasset_secret_key_0123456789'; }, /** * 마스터 키를 DB에 저장하기 전 암호화하는 함수 */ encryptMasterKey(plainKey) { return this._internalProcess(plainKey, true); }, clearCache() { cachedKey = null; }, /** * Encrypt with specific key (optional, defaults to master) */ async encrypt(text, customKey = null) { if (!text) return text; try { const secret = customKey || await this.getMasterKey(); const keyBuffer = crypto.scryptSync(secret, 'salt', 32); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(ALGORITHM, keyBuffer, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); return iv.toString('hex') + ':' + encrypted; } catch (e) { console.error('Encryption failed:', e.message); return text; } }, /** * Decrypt with specific key (optional, defaults to master) */ async decrypt(text, customKey = null) { if (!text || !text.includes(':')) return text; try { const secret = customKey || await this.getMasterKey(); const keyBuffer = crypto.scryptSync(secret, 'salt', 32); const [ivHex, cipherText] = text.split(':'); if (!ivHex || ivHex.length !== 32) return text; const iv = Buffer.from(ivHex, 'hex'); const decipher = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv); let decrypted = decipher.update(cipherText, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } catch (e) { return text; } } }; module.exports = cryptoUtil;