| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- 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<AESConfig>): 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<AESConfig>): 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
- }
|