import CryptoJS from "crypto-js"; /** * 加密配置接口 */ export interface CryptoConfig { key: string; iv: string; } /** * AES 加密工具类 * 使用 AES-128-CBC 算法进行加解密 */ export class CryptoUtils { private static readonly KEY_LENGTH = 16; private static readonly IV_LENGTH = 16; /** * 验证密钥和向量的长度 * @param key AES 密钥(16字节) * @param iv AES 向量(16字节) * @returns 是否有效 */ static validateConfig(key: string, iv: string): boolean { return key.length === this.KEY_LENGTH && iv.length === this.IV_LENGTH; } /** * 加密数据 * @param data 待加密的数据(对象或字符串) * @param key AES 密钥(16字节) * @param iv AES 向量(16字节) * @returns Base64 编码的加密字符串 */ static encrypt(data: any, key: string, iv: string): string { if (!this.validateConfig(key, iv)) { throw new Error(`Invalid crypto config: key must be ${this.KEY_LENGTH} bytes, iv must be ${this.IV_LENGTH} bytes`); } try { // 将对象转换为 JSON 字符串 const plainText = typeof data === "object" ? JSON.stringify(data) : String(data); // 将密钥和向量转换为 WordArray const keyWordArray = CryptoJS.enc.Utf8.parse(key); const ivWordArray = CryptoJS.enc.Utf8.parse(iv); // 使用 AES-128-CBC 加密 const encrypted = CryptoJS.AES.encrypt(plainText, keyWordArray, { iv: ivWordArray, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 返回 Base64 编码的密文 return encrypted.toString(); } catch (error) { throw new Error(`Encryption failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * 解密数据 * @param ciphertext Base64 编码的密文 * @param key AES 密钥(16字节) * @param iv AES 向量(16字节) * @returns 解密后的数据(对象或字符串) */ static decrypt(ciphertext: string, key: string, iv: string): any { if (!this.validateConfig(key, iv)) { throw new Error(`Invalid crypto config: key must be ${this.KEY_LENGTH} bytes, iv must be ${this.IV_LENGTH} bytes`); } try { // 将密钥和向量转换为 WordArray const keyWordArray = CryptoJS.enc.Utf8.parse(key); const ivWordArray = CryptoJS.enc.Utf8.parse(iv); // 使用 AES-128-CBC 解密 const decrypted = CryptoJS.AES.decrypt(ciphertext, keyWordArray, { iv: ivWordArray, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 将解密结果转换为 UTF-8 字符串 const result = decrypted.toString(CryptoJS.enc.Utf8); // 如果解密结果为空,返回空字符串 if (!result) { return ""; } // 尝试解析为 JSON 对象 try { return JSON.parse(result); } catch (e) { // 如果不是 JSON,则直接返回字符串 return result; } } catch (error) { throw new Error(`Decryption failed: ${error instanceof Error ? error.message : String(error)}`); } } } /** * 默认加密工具实例 */ export const crypto = { /** * 加密数据(使用默认配置) */ encrypt(data: any, config?: Partial): string { const key = config?.key || import.meta.env.VITE_API_ENCRYPTION_KEY || ""; const iv = config?.iv || import.meta.env.VITE_API_ENCRYPTION_IV || ""; if (!key || !iv) { console.warn("Encryption keys not configured, returning plaintext"); return typeof data === "object" ? JSON.stringify(data) : String(data); } return CryptoUtils.encrypt(data, key, iv); }, /** * 解密数据(使用默认配置) */ decrypt(ciphertext: string, config?: Partial): any { const key = config?.key || import.meta.env.VITE_API_ENCRYPTION_KEY || ""; const iv = config?.iv || import.meta.env.VITE_API_ENCRYPTION_IV || ""; if (!key || !iv) { console.warn("Decryption keys not configured, returning ciphertext"); return ciphertext; } return CryptoUtils.decrypt(ciphertext, key, iv); }, /** * 检查是否启用了加密配置 */ isConfigured(): boolean { const key = import.meta.env.VITE_API_ENCRYPTION_KEY || ""; const iv = import.meta.env.VITE_API_ENCRYPTION_IV || ""; return CryptoUtils.validateConfig(key, iv); } };