import CryptoJS from 'crypto-js' import JSEncrypt from 'jsencrypt' /** * 加密工具类 * 提供AES和RSA加密解密功能 */ class EncryptUtil { /** * RSA公钥 */ private readonly RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqPvovSfXcwBbW8cKMCgwqNpsYuzF8RPAPFb7LGsnVo44JhM/xxzDyzoYtdfNmtbIuKVi9PzIsyp6rg+09gbuI6UGwBZ5DWBDBMqv5MPdOF5dCQkB2Bbr5yPfURPENypUz+pBFBg41d+BC+rwRiXELwKy7Y9caD/MtJyHydj8OUwIDAQAB' /** * RSA私钥 */ private readonly RSA_PRIVATE_KEY = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKo++i9J9dzAFtbxwowKDCo2mxi7MXxE8A8VvssaydWjjgmEz/HHMPLOhi1182a1si4pWL0/MizKnquD7T2Bu4jpQbAFnkNYEMEyq/kw904Xl0JCQHYFuvnI99RE8Q3KlTP6kEUGDjV34EL6vBGJcQvArLtj1xoP8y0nIfJ2Pw5TAgMBAAECgYAGGB8IllMwxceLhjf6n1l0IWRH7FuHIUieoZ6k0p6rASHSgWiYNRMxfecbtX8zDAoG0QAWNi7rn40ygpR5gS1fWDAKhmnhKgQIT6wW0VmD4hraaeyP78iy8BLhlvblri2nCPIhDH5+l96v7D47ZZi3ZSOzcj89s1eS/k7/N4peEQJBAPEtGGJY+lBoCxQMhGyzuzDmgcS1Un1ZE2pt+XNCVl2b+T8fxWJH3tRRR8wOY5uvtPiK1HM/IjT0T5qwQeH8Yk0CQQC0tcv3d/bDb7bOe9QzUFDQkUSpTdPWAgMX2OVPxjdq3Sls9oA5+fGNYEy0OgyqTjde0b4iRzlD1O0OhLqPSUMfAkEAh5FIvqezdRU2/PsYSR4yoAdCdLdT+h/jGRVefhqQ/6eYUJJkWp15tTFHQX3pIe9/s6IeT/XyHYAjaxmevxAmlQJBAKSdhvQjf9KAjZKDEsa7vyJ/coCXuQUWSCMNHbcR5aGfXgE4e45UtUoIE1eKGcd6AM6LWhx3rR6xdFDpb9je8BkCQB0SpevGfOQkMk5i8xkEt9eeYP0fi8nv6eOUcK96EXbzs4jV2SAoQJ9oJegPtPROHbhIvVUmNQTbuP10Yjg59+8=' /** * AES ECB模式加密 * @param word 待加密的字符串 * @param encryKey 加密密钥 * @returns 加密后的字符串 */ AESEncrypt(word: string, encryKey: string): string { try { const key = CryptoJS.enc.Utf8.parse(encryKey) const srcs = CryptoJS.enc.Utf8.parse(word) const encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, }) return encrypted.toString() } catch (error) { throw new Error(`AES ECB加密失败: ${error}`) } } /** * AES ECB模式解密 * @param word 待解密的字符串 * @param encryKey 解密密钥 * @returns 解密后的数据(可能是字符串或对象) */ AESDecrypt(word: string, encryKey: string): string | object { try { const key = CryptoJS.enc.Utf8.parse(encryKey) const decrypt = CryptoJS.AES.decrypt(word, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, }) const ret = CryptoJS.enc.Utf8.stringify(decrypt).toString() try { return JSON.parse(ret) } catch { return ret } } catch (error) { throw new Error(`AES ECB解密失败: ${error}`) } } /** * RSA公钥加密 * * @param data 需要加密的数据 * @param publicKey PEM格式的公钥 * @returns Base64编码的加密结果 */ RSAEncrypt(data: string, publicKey: string): string { try { const encryptor = new JSEncrypt() encryptor.setPublicKey(publicKey) const encrypted = encryptor.encrypt(data) if (!encrypted) { throw new Error('加密失败:空结果') } return encrypted } catch (error) { console.error('RSA加密错误:', error) throw new Error('RSA加密失败,请检查公钥格式') } } /** * RSA私钥解密 * * @param encryptedData Base64编码的加密数据 * @returns 解密后的原始字符串 */ RSADecrypt(encryptedData: string): string { try { const decryptor = new JSEncrypt() decryptor.setPrivateKey(import.meta.env.VITE_RSA_PRIVATE_KEY) const decrypted = decryptor.decrypt(encryptedData) if (!decrypted) { throw new Error('解密失败:空结果') } return decrypted } catch (error) { console.error('RSA解密错误:', error) throw new Error('RSA解密失败,请检查私钥格式') } } /** * AES CBC模式加密 * @param data 待加密的数据 * @param cryptoKey 十六进制格式的加密密钥 * @returns 组合格式的加密结果 */ AESEncryptCBC(data: any, cryptoKey: string): string { try { // 生成随机IV(每次加密不同) const iv = CryptoJS.lib.WordArray.random(16) // 使用 AES CBC 模式进行加密 const encrypted = CryptoJS.AES.encrypt( JSON.stringify(data), CryptoJS.enc.Hex.parse(cryptoKey), { iv, // IV mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, }, ) // 组合IV和密文,分别Base64编码:IV(base64) + : + 密文(base64) const ivBase64 = iv.toString(CryptoJS.enc.Base64) const encryptedBase64 = encrypted.toString() return `${ivBase64}:${encryptedBase64}` } catch (error) { throw new Error(`AES CBC加密失败: ${error}`) } } /** * AES CBC模式解密 * @param combinedText 格式:Base64(iv):Base64(ciphertext) * @param hexKey 十六进制格式的加密密钥 * @returns 解密后的原始字符串 */ AESDecryptCBC(combinedText: string, hexKey: string): string { try { // 分离 IV 和 密文 const parts = combinedText.split(':') if (parts.length !== 2) { throw new Error('Invalid encrypted data format') } const ivBase64 = parts[0] const encryptedBase64 = parts[1] // 解析IV和密文 const iv = CryptoJS.enc.Base64.parse(ivBase64) const key = CryptoJS.enc.Hex.parse(hexKey) // 解密 const decrypted = CryptoJS.AES.decrypt(encryptedBase64, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, }) // 将解密后的数据转换为字符串 return decrypted.toString(CryptoJS.enc.Utf8) } catch (error) { throw new Error(`AES-CBC解密失败: ${error}`) } } /** * 安全的AES-CBC解密(失败时返回null) * 用于响应自动解密,解密失败时不抛出异常 * @param combinedText 格式:Base64(iv):Base64(ciphertext) * @param hexKey 十六进制格式的密钥 * @returns 解密后的字符串,失败时返回null */ safeAESDecryptCBC(combinedText: string, hexKey: string): string | null { try { return this.AESDecryptCBC(combinedText, hexKey) } catch { // AES-CBC解密失败,静默处理 return null } } /** * 智能响应解密 * 自动检测响应数据格式并进行解密 * @param responseData 响应数据 * @param sessionKey 会话密钥 * @returns 解密后的数据,失败时返回原数据 */ decryptResponse(responseData: any, sessionKey: string): any { try { let dataStr = '' // 处理不同类型的响应数据 if (typeof responseData === 'string') { dataStr = responseData } else if (typeof responseData === 'object') { dataStr = JSON.stringify(responseData) } else { dataStr = String(responseData) } // 尝试解密 const decryptedStr = this.safeAESDecryptCBC(dataStr, sessionKey) if (decryptedStr) { try { return JSON.parse(decryptedStr) } catch { return decryptedStr } } } catch (error) { console.warn('响应解密处理失败:', error) // 响应解密处理失败,静默处理 } // 解密失败或数据格式不正确时,返回原数据 return responseData } } // 创建单例实例 const encryptUtil = new EncryptUtil() export { EncryptUtil, encryptUtil }