paper-burner/local-proxy/db/crypto.js

118 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 加密工具模块
* 用于安全地加密和解密敏感数据(如 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');
}