import crypto from 'crypto'; class EncryptionService { private readonly algorithm = 'aes-256-gcm'; private readonly key: Buffer; constructor() { const keyString = process.env.ENCRYPTION_KEY; if (!keyString) { throw new Error('ENCRYPTION_KEY environment variable is required'); } this.key = crypto.scryptSync(keyString, 'salt', 32); } encrypt(text: string): string { const iv = crypto.randomBytes(16); const cipher = crypto.createCipher(this.algorithm, this.key); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted; } decrypt(encryptedText: string): string { const [ivHex, authTagHex, encrypted] = encryptedText.split(':'); if (!ivHex || !authTagHex || !encrypted) { throw new Error('Invalid encrypted text format'); } const iv = Buffer.from(ivHex, 'hex'); const authTag = Buffer.from(authTagHex, 'hex'); const decipher = crypto.createDecipher(this.algorithm, this.key); decipher.setAuthTag(authTag); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } // 비밀번호 해싱 (bcrypt 대신 사용) hashPassword(password: string): string { const salt = crypto.randomBytes(16).toString('hex'); const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex'); return salt + ':' + hash; } verifyPassword(password: string, hashedPassword: string): boolean { const [salt, hash] = hashedPassword.split(':'); const verifyHash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex'); return hash === verifyHash; } // 랜덤 토큰 생성 generateToken(length: number = 32): string { return crypto.randomBytes(length).toString('hex'); } // HMAC 서명 생성 createHmac(data: string, secret: string): string { return crypto.createHmac('sha256', secret).update(data).digest('hex'); } // HMAC 검증 verifyHmac(data: string, signature: string, secret: string): boolean { const expectedSignature = this.createHmac(data, secret); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature)); } } export const encryptionService = new EncryptionService();