import CryptoJS from 'crypto-js' /** * 增强版AES CFB加密解密工具 * 采用CFB模式,无需填充,解决CBC模式填充问题 */ // 配置接口 export interface AESConfig { key: string iv: string mode: typeof CryptoJS.mode.CFB padding: typeof CryptoJS.pad.NoPadding } // 加密结果接口 export interface EncryptResult { encryptedData: string iv: string timestamp: number version: string } // 后端使用的十六进制密钥和IV(与后端完全匹配) const BACKEND_AES_KEY = "e36fa8396463df288532c5f1adc4c9fb960ec8decff0d2856cd94ad340638e6f" const BACKEND_AES_IV = "1c16606009b8e3630ddbaeb06c831099" // 默认配置 const DEFAULT_CONFIG: AESConfig = { key: BACKEND_AES_KEY, iv: BACKEND_AES_IV, mode: CryptoJS.mode.CFB, padding: CryptoJS.pad.NoPadding } /** * 密钥和IV处理函数 * 后端使用十六进制格式,需要将十六进制字符串转换为字节数组 */ function processKeyAndIV(config: AESConfig): { key: CryptoJS.lib.WordArray; iv: CryptoJS.lib.WordArray } { // 检查是否为十六进制字符串(后端格式) const isHexKey = /^[0-9a-fA-F]+$/.test(config.key) const isHexIV = /^[0-9a-fA-F]+$/.test(config.iv) if (isHexKey && isHexIV) { // 后端格式:十六进制字符串,直接解析为字节数组 return { key: CryptoJS.enc.Hex.parse(config.key), iv: CryptoJS.enc.Hex.parse(config.iv) } } else { // 前端格式:字符串,使用UTF-8编码 // 密钥处理:确保32字节长度 const processedKey = config.key.padEnd(32, '0').substring(0, 32) // IV处理:确保16字节长度 const processedIV = config.iv.padEnd(16, '0').substring(0, 16) return { key: CryptoJS.enc.Utf8.parse(processedKey), iv: CryptoJS.enc.Utf8.parse(processedIV) } } } /** * 验证配置有效性 */ function validateConfig(config: AESConfig): void { if (!config.key || config.key.trim().length === 0) { throw new Error('AES密钥不能为空') } if (!config.iv || config.iv.trim().length === 0) { throw new Error('AES IV不能为空') } // 检查是否为十六进制格式 const isHexKey = /^[0-9a-fA-F]+$/.test(config.key) const isHexIV = /^[0-9a-fA-F]+$/.test(config.iv) if (isHexKey && isHexIV) { // 十六进制格式:密钥应为64字符(32字节),IV应为32字符(16字节) if (config.key.length !== 64) { throw new Error('十六进制AES密钥长度必须为64字符(32字节)') } if (config.iv.length !== 32) { throw new Error('十六进制AES IV长度必须为32字符(16字节)') } } else { // 字符串格式:长度要求 if (config.key.length < 16) { throw new Error('AES密钥长度至少16位') } if (config.iv.length < 16) { throw new Error('AES IV长度至少16位') } } } /** * 增强版AES加密函数 * @param data 需要加密的数据 * @param customConfig 自定义配置(可选) * @returns 加密结果对象 */ export function enhancedEncrypt(data: string | object, customConfig?: Partial): EncryptResult { try { if (!data) { throw new Error('加密数据不能为空') } // 合并配置 const config: AESConfig = { ...DEFAULT_CONFIG, ...customConfig } // 验证配置 validateConfig(config) // 处理密钥和IV const { key, iv } = processKeyAndIV(config) // 准备明文数据 const plaintext = typeof data === 'object' ? JSON.stringify(data) : String(data) // 对数据进行UTF-8编码 const srcs = CryptoJS.enc.Utf8.parse(plaintext) // 执行AES CFB加密 const encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: config.mode, padding: config.padding }) // 返回完整的加密结果 return { encryptedData: CryptoJS.enc.Base64.stringify(encrypted.ciphertext), iv: CryptoJS.enc.Base64.stringify(iv), timestamp: Date.now(), version: '3.0' // 更新版本号以标识CFB模式 } } catch (error) { console.error('AES加密失败:', error) throw new Error(`加密失败: ${error instanceof Error ? error.message : '未知错误'}`) } } /** * 增强版AES解密函数 * @param encryptedData 加密的Base64字符串 * @param iv IV的Base64字符串 * @param customConfig 自定义配置(可选) * @returns 解密后的原始数据 */ export function enhancedDecrypt(encryptedData: string, iv: string, customConfig?: Partial): string | object { try { if (!encryptedData || !iv) { throw new Error('解密数据或IV不能为空') } // 合并配置 const config: AESConfig = { ...DEFAULT_CONFIG, ...customConfig } // 验证配置 validateConfig(config) // 处理密钥 const { key } = processKeyAndIV(config) // 解析IV const aesIv = CryptoJS.enc.Base64.parse(iv) // 执行AES CFB解密 const decrypt = CryptoJS.AES.decrypt(encryptedData, key, { iv: aesIv, mode: config.mode, padding: config.padding }) // 转换为UTF-8字符串 const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8) if (!decryptedStr) { throw new Error('解密失败,可能是密钥错误或数据损坏') } // 尝试解析为JSON对象,如果不是JSON则返回字符串 try { return JSON.parse(decryptedStr) } catch { return decryptedStr } } catch (error) { console.error('AES解密失败:', error) throw new Error(`解密失败: ${error instanceof Error ? error.message : '未知错误'}`) } } /** * 加密敏感数据(业务专用) * @param sensitiveData 敏感数据对象 * @returns 加密结果 */ export function encryptSensitiveData(sensitiveData: object): EncryptResult { const dataToEncrypt = { ...sensitiveData, encryptedAt: new Date().toISOString(), version: '3.0', securityLevel: 'high' } return enhancedEncrypt(dataToEncrypt) } /** * 与后端完全匹配的加密函数 * 使用后端相同的十六进制密钥和IV,确保加密解密完全匹配 * @param plaintext 明文数据 * @returns 加密后的Base64字符串 */ export function encryptWithBackendConfig(plaintext: string): string { try { if (!plaintext) { throw new Error('加密数据不能为空') } // 使用后端配置 const config: AESConfig = { ...DEFAULT_CONFIG } // 验证配置 validateConfig(config) // 处理密钥和IV(十六进制格式) const { key, iv } = processKeyAndIV(config) // 对数据进行UTF-8编码 const srcs = CryptoJS.enc.Utf8.parse(plaintext) // 执行AES CFB加密 const encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: config.mode, padding: config.padding }) // 返回Base64编码的加密数据 return CryptoJS.enc.Base64.stringify(encrypted.ciphertext) } catch (error) { console.error('后端匹配加密失败:', error) throw new Error(`加密失败: ${error instanceof Error ? error.message : '未知错误'}`) } } /** * 与后端完全匹配的解密函数 * @param encryptedData 加密的Base64字符串 * @returns 解密后的原始数据 */ export function decryptWithBackendConfig(encryptedData: string): string { try { if (!encryptedData) { throw new Error('解密数据不能为空') } // 使用后端配置 const config: AESConfig = { ...DEFAULT_CONFIG } // 验证配置 validateConfig(config) // 处理密钥 const { key, iv } = processKeyAndIV(config) // 执行AES CFB解密 const decrypt = CryptoJS.AES.decrypt(encryptedData, key, { iv: iv, mode: config.mode, padding: config.padding }) // 转换为UTF-8字符串 const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8) if (!decryptedStr) { throw new Error('解密失败,可能是密钥错误或数据损坏') } return decryptedStr } catch (error) { console.error('后端匹配解密失败:', error) throw new Error(`解密失败: ${error instanceof Error ? error.message : '未知错误'}`) } } /** * 解密登录数据 * @param loginData 登录数据对象 * @returns 解密后的登录信息 */ export function decryptLoginData(loginData: { aesPassword: string; saltValue: string }): any { try { // 使用saltValue作为IV进行解密 return enhancedDecrypt(loginData.aesPassword, loginData.saltValue) } catch (error) { console.error('登录数据解密失败:', error) throw error } } /** * 验证加密数据完整性 * @param encryptResult 加密结果 * @returns 是否有效 */ export function validateEncryptedData(encryptResult: EncryptResult): boolean { try { if (!encryptResult.encryptedData || !encryptResult.iv) { return false } // 检查时间戳是否合理(不超过24小时) const now = Date.now() const timeDiff = now - encryptResult.timestamp if (timeDiff > 24 * 60 * 60 * 1000) { return false } // 检查版本号(支持2.0和3.0版本) if (!encryptResult.version || (encryptResult.version !== '2.0' && encryptResult.version !== '3.0')) { return false } return true } catch { return false } } /** * 生成安全的随机密钥 * @param length 密钥长度 * @returns 随机密钥 */ export function generateSecureKey(length: number = 32): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?' const crypto = window.crypto || (window as any).msCrypto if (crypto && crypto.getRandomValues) { // 使用Web Crypto API生成更安全的随机数 const array = new Uint32Array(length) crypto.getRandomValues(array) let result = '' for (let i = 0; i < length; i++) { result += chars[array[i] % chars.length] } return result } else { // 回退方案 let result = '' for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)) } return result } } export default { enhancedEncrypt, enhancedDecrypt, encryptSensitiveData, decryptLoginData, validateEncryptedData, generateSecureKey }