118 lines
3.1 KiB
JavaScript
118 lines
3.1 KiB
JavaScript
/**
|
||
* 加密工具模块
|
||
* 用于安全地加密和解密敏感数据(如 API Keys)
|
||
*/
|
||
|
||
import crypto from 'crypto';
|
||
|
||
// 加密常量
|
||
const ALGORITHM = 'aes-256-gcm';
|
||
const IV_LENGTH = 16;
|
||
const TAG_LENGTH = 16;
|
||
const KEY_LENGTH = 32;
|
||
const PBKDF2_ITERATIONS = 100000;
|
||
|
||
/**
|
||
* 获取加密 salt
|
||
*/
|
||
function getEncryptionSalt() {
|
||
if (process.env.ENCRYPTION_SALT) {
|
||
return process.env.ENCRYPTION_SALT;
|
||
}
|
||
|
||
if (process.env.NODE_ENV === 'production') {
|
||
throw new Error('ENCRYPTION_SALT must be set in production environment');
|
||
}
|
||
|
||
console.warn('⚠️ Using default encryption salt for development. Set ENCRYPTION_SALT for production.');
|
||
return 'dev-salt-fixed-for-development';
|
||
}
|
||
|
||
/**
|
||
* 从环境变量获取加密密钥
|
||
*/
|
||
function getEncryptionKey() {
|
||
const secret = process.env.ENCRYPTION_SECRET || process.env.ENCRYPTION_KEY || 'default-encryption-key-change-in-production';
|
||
|
||
return crypto.pbkdf2Sync(secret, getEncryptionSalt(), PBKDF2_ITERATIONS, KEY_LENGTH, 'sha256');
|
||
}
|
||
|
||
/**
|
||
* 加密文本
|
||
* @param {string} text - 要加密的明文
|
||
* @returns {string} 加密后的数据(Base64 编码)
|
||
*/
|
||
export function encrypt(text) {
|
||
if (!text) return text;
|
||
|
||
try {
|
||
const key = getEncryptionKey();
|
||
const iv = crypto.randomBytes(IV_LENGTH);
|
||
|
||
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
||
|
||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||
encrypted += cipher.final('hex');
|
||
|
||
const tag = cipher.getAuthTag();
|
||
|
||
// 组合 IV + 加密数据 + 认证标签
|
||
const combined = Buffer.concat([
|
||
iv,
|
||
Buffer.from(encrypted, 'hex'),
|
||
tag
|
||
]);
|
||
|
||
return combined.toString('base64');
|
||
} catch (error) {
|
||
console.error('Encryption error:', error);
|
||
throw new Error('Failed to encrypt data');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解密文本
|
||
* @param {string} encryptedData - 加密的数据(Base64 编码)
|
||
* @returns {string} 解密后的明文
|
||
*/
|
||
export function decrypt(encryptedData) {
|
||
if (!encryptedData) return encryptedData;
|
||
|
||
try {
|
||
const key = getEncryptionKey();
|
||
const combined = Buffer.from(encryptedData, 'base64');
|
||
|
||
// 提取 IV、加密数据和认证标签
|
||
const iv = combined.subarray(0, IV_LENGTH);
|
||
const tag = combined.subarray(combined.length - TAG_LENGTH);
|
||
const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
|
||
|
||
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
||
decipher.setAuthTag(tag);
|
||
|
||
let decrypted = decipher.update(encrypted, undefined, 'utf8');
|
||
decrypted += decipher.final('utf8');
|
||
|
||
return decrypted;
|
||
} catch (error) {
|
||
console.error('Decryption error:', error);
|
||
throw new Error('Failed to decrypt data');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成随机加密密钥(用于初始化)
|
||
* @returns {string} Base64 编码的随机密钥
|
||
*/
|
||
export function generateEncryptionSecret() {
|
||
return crypto.randomBytes(32).toString('base64');
|
||
}
|
||
|
||
/**
|
||
* 哈希敏感数据(单向,用于比较)
|
||
* @param {string} data - 要哈希的数据
|
||
* @returns {string} 哈希值
|
||
*/
|
||
export function hash(data) {
|
||
return crypto.createHash('sha256').update(data).digest('hex');
|
||
} |