aesCrypto.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. import CryptoJS from 'crypto-js'
  2. /**
  3. * 增强版AES CFB加密解密工具
  4. * 采用CFB模式,无需填充,解决CBC模式填充问题
  5. */
  6. // 配置接口
  7. export interface AESConfig {
  8. key: string
  9. iv: string
  10. mode: typeof CryptoJS.mode.CFB
  11. padding: typeof CryptoJS.pad.NoPadding
  12. }
  13. // 加密结果接口
  14. export interface EncryptResult {
  15. encryptedData: string
  16. iv: string
  17. timestamp: number
  18. version: string
  19. }
  20. // 后端使用的十六进制密钥和IV(与后端完全匹配)
  21. const BACKEND_AES_KEY = "e36fa8396463df288532c5f1adc4c9fb960ec8decff0d2856cd94ad340638e6f"
  22. const BACKEND_AES_IV = "1c16606009b8e3630ddbaeb06c831099"
  23. // 默认配置
  24. const DEFAULT_CONFIG: AESConfig = {
  25. key: BACKEND_AES_KEY,
  26. iv: BACKEND_AES_IV,
  27. mode: CryptoJS.mode.CFB,
  28. padding: CryptoJS.pad.NoPadding
  29. }
  30. /**
  31. * 密钥和IV处理函数
  32. * 后端使用十六进制格式,需要将十六进制字符串转换为字节数组
  33. */
  34. function processKeyAndIV(config: AESConfig): { key: CryptoJS.lib.WordArray; iv: CryptoJS.lib.WordArray } {
  35. // 检查是否为十六进制字符串(后端格式)
  36. const isHexKey = /^[0-9a-fA-F]+$/.test(config.key)
  37. const isHexIV = /^[0-9a-fA-F]+$/.test(config.iv)
  38. if (isHexKey && isHexIV) {
  39. // 后端格式:十六进制字符串,直接解析为字节数组
  40. return {
  41. key: CryptoJS.enc.Hex.parse(config.key),
  42. iv: CryptoJS.enc.Hex.parse(config.iv)
  43. }
  44. } else {
  45. // 前端格式:字符串,使用UTF-8编码
  46. // 密钥处理:确保32字节长度
  47. const processedKey = config.key.padEnd(32, '0').substring(0, 32)
  48. // IV处理:确保16字节长度
  49. const processedIV = config.iv.padEnd(16, '0').substring(0, 16)
  50. return {
  51. key: CryptoJS.enc.Utf8.parse(processedKey),
  52. iv: CryptoJS.enc.Utf8.parse(processedIV)
  53. }
  54. }
  55. }
  56. /**
  57. * 验证配置有效性
  58. */
  59. function validateConfig(config: AESConfig): void {
  60. if (!config.key || config.key.trim().length === 0) {
  61. throw new Error('AES密钥不能为空')
  62. }
  63. if (!config.iv || config.iv.trim().length === 0) {
  64. throw new Error('AES IV不能为空')
  65. }
  66. // 检查是否为十六进制格式
  67. const isHexKey = /^[0-9a-fA-F]+$/.test(config.key)
  68. const isHexIV = /^[0-9a-fA-F]+$/.test(config.iv)
  69. if (isHexKey && isHexIV) {
  70. // 十六进制格式:密钥应为64字符(32字节),IV应为32字符(16字节)
  71. if (config.key.length !== 64) {
  72. throw new Error('十六进制AES密钥长度必须为64字符(32字节)')
  73. }
  74. if (config.iv.length !== 32) {
  75. throw new Error('十六进制AES IV长度必须为32字符(16字节)')
  76. }
  77. } else {
  78. // 字符串格式:长度要求
  79. if (config.key.length < 16) {
  80. throw new Error('AES密钥长度至少16位')
  81. }
  82. if (config.iv.length < 16) {
  83. throw new Error('AES IV长度至少16位')
  84. }
  85. }
  86. }
  87. /**
  88. * 增强版AES加密函数
  89. * @param data 需要加密的数据
  90. * @param customConfig 自定义配置(可选)
  91. * @returns 加密结果对象
  92. */
  93. export function enhancedEncrypt(data: string | object, customConfig?: Partial<AESConfig>): EncryptResult {
  94. try {
  95. if (!data) {
  96. throw new Error('加密数据不能为空')
  97. }
  98. // 合并配置
  99. const config: AESConfig = { ...DEFAULT_CONFIG, ...customConfig }
  100. // 验证配置
  101. validateConfig(config)
  102. // 处理密钥和IV
  103. const { key, iv } = processKeyAndIV(config)
  104. // 准备明文数据
  105. const plaintext = typeof data === 'object' ? JSON.stringify(data) : String(data)
  106. // 对数据进行UTF-8编码
  107. const srcs = CryptoJS.enc.Utf8.parse(plaintext)
  108. // 执行AES CFB加密
  109. const encrypted = CryptoJS.AES.encrypt(srcs, key, {
  110. iv: iv,
  111. mode: config.mode,
  112. padding: config.padding
  113. })
  114. // 返回完整的加密结果
  115. return {
  116. encryptedData: CryptoJS.enc.Base64.stringify(encrypted.ciphertext),
  117. iv: CryptoJS.enc.Base64.stringify(iv),
  118. timestamp: Date.now(),
  119. version: '3.0' // 更新版本号以标识CFB模式
  120. }
  121. } catch (error) {
  122. console.error('AES加密失败:', error)
  123. throw new Error(`加密失败: ${error instanceof Error ? error.message : '未知错误'}`)
  124. }
  125. }
  126. /**
  127. * 增强版AES解密函数
  128. * @param encryptedData 加密的Base64字符串
  129. * @param iv IV的Base64字符串
  130. * @param customConfig 自定义配置(可选)
  131. * @returns 解密后的原始数据
  132. */
  133. export function enhancedDecrypt(encryptedData: string, iv: string, customConfig?: Partial<AESConfig>): string | object {
  134. try {
  135. if (!encryptedData || !iv) {
  136. throw new Error('解密数据或IV不能为空')
  137. }
  138. // 合并配置
  139. const config: AESConfig = { ...DEFAULT_CONFIG, ...customConfig }
  140. // 验证配置
  141. validateConfig(config)
  142. // 处理密钥
  143. const { key } = processKeyAndIV(config)
  144. // 解析IV
  145. const aesIv = CryptoJS.enc.Base64.parse(iv)
  146. // 执行AES CFB解密
  147. const decrypt = CryptoJS.AES.decrypt(encryptedData, key, {
  148. iv: aesIv,
  149. mode: config.mode,
  150. padding: config.padding
  151. })
  152. // 转换为UTF-8字符串
  153. const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
  154. if (!decryptedStr) {
  155. throw new Error('解密失败,可能是密钥错误或数据损坏')
  156. }
  157. // 尝试解析为JSON对象,如果不是JSON则返回字符串
  158. try {
  159. return JSON.parse(decryptedStr)
  160. } catch {
  161. return decryptedStr
  162. }
  163. } catch (error) {
  164. console.error('AES解密失败:', error)
  165. throw new Error(`解密失败: ${error instanceof Error ? error.message : '未知错误'}`)
  166. }
  167. }
  168. /**
  169. * 加密敏感数据(业务专用)
  170. * @param sensitiveData 敏感数据对象
  171. * @returns 加密结果
  172. */
  173. export function encryptSensitiveData(sensitiveData: object): EncryptResult {
  174. const dataToEncrypt = {
  175. ...sensitiveData,
  176. encryptedAt: new Date().toISOString(),
  177. version: '3.0',
  178. securityLevel: 'high'
  179. }
  180. return enhancedEncrypt(dataToEncrypt)
  181. }
  182. /**
  183. * 与后端完全匹配的加密函数
  184. * 使用后端相同的十六进制密钥和IV,确保加密解密完全匹配
  185. * @param plaintext 明文数据
  186. * @returns 加密后的Base64字符串
  187. */
  188. export function encryptWithBackendConfig(plaintext: string): string {
  189. try {
  190. if (!plaintext) {
  191. throw new Error('加密数据不能为空')
  192. }
  193. // 使用后端配置
  194. const config: AESConfig = { ...DEFAULT_CONFIG }
  195. // 验证配置
  196. validateConfig(config)
  197. // 处理密钥和IV(十六进制格式)
  198. const { key, iv } = processKeyAndIV(config)
  199. // 对数据进行UTF-8编码
  200. const srcs = CryptoJS.enc.Utf8.parse(plaintext)
  201. // 执行AES CFB加密
  202. const encrypted = CryptoJS.AES.encrypt(srcs, key, {
  203. iv: iv,
  204. mode: config.mode,
  205. padding: config.padding
  206. })
  207. // 返回Base64编码的加密数据
  208. return CryptoJS.enc.Base64.stringify(encrypted.ciphertext)
  209. } catch (error) {
  210. console.error('后端匹配加密失败:', error)
  211. throw new Error(`加密失败: ${error instanceof Error ? error.message : '未知错误'}`)
  212. }
  213. }
  214. /**
  215. * 与后端完全匹配的解密函数
  216. * @param encryptedData 加密的Base64字符串
  217. * @returns 解密后的原始数据
  218. */
  219. export function decryptWithBackendConfig(encryptedData: string): string {
  220. try {
  221. if (!encryptedData) {
  222. throw new Error('解密数据不能为空')
  223. }
  224. // 使用后端配置
  225. const config: AESConfig = { ...DEFAULT_CONFIG }
  226. // 验证配置
  227. validateConfig(config)
  228. // 处理密钥
  229. const { key, iv } = processKeyAndIV(config)
  230. // 执行AES CFB解密
  231. const decrypt = CryptoJS.AES.decrypt(encryptedData, key, {
  232. iv: iv,
  233. mode: config.mode,
  234. padding: config.padding
  235. })
  236. // 转换为UTF-8字符串
  237. const decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
  238. if (!decryptedStr) {
  239. throw new Error('解密失败,可能是密钥错误或数据损坏')
  240. }
  241. return decryptedStr
  242. } catch (error) {
  243. console.error('后端匹配解密失败:', error)
  244. throw new Error(`解密失败: ${error instanceof Error ? error.message : '未知错误'}`)
  245. }
  246. }
  247. /**
  248. * 解密登录数据
  249. * @param loginData 登录数据对象
  250. * @returns 解密后的登录信息
  251. */
  252. export function decryptLoginData(loginData: { aesPassword: string; saltValue: string }): any {
  253. try {
  254. // 使用saltValue作为IV进行解密
  255. return enhancedDecrypt(loginData.aesPassword, loginData.saltValue)
  256. } catch (error) {
  257. console.error('登录数据解密失败:', error)
  258. throw error
  259. }
  260. }
  261. /**
  262. * 验证加密数据完整性
  263. * @param encryptResult 加密结果
  264. * @returns 是否有效
  265. */
  266. export function validateEncryptedData(encryptResult: EncryptResult): boolean {
  267. try {
  268. if (!encryptResult.encryptedData || !encryptResult.iv) {
  269. return false
  270. }
  271. // 检查时间戳是否合理(不超过24小时)
  272. const now = Date.now()
  273. const timeDiff = now - encryptResult.timestamp
  274. if (timeDiff > 24 * 60 * 60 * 1000) {
  275. return false
  276. }
  277. // 检查版本号(支持2.0和3.0版本)
  278. if (!encryptResult.version || (encryptResult.version !== '2.0' && encryptResult.version !== '3.0')) {
  279. return false
  280. }
  281. return true
  282. } catch {
  283. return false
  284. }
  285. }
  286. /**
  287. * 生成安全的随机密钥
  288. * @param length 密钥长度
  289. * @returns 随机密钥
  290. */
  291. export function generateSecureKey(length: number = 32): string {
  292. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?'
  293. const crypto = window.crypto || (window as any).msCrypto
  294. if (crypto && crypto.getRandomValues) {
  295. // 使用Web Crypto API生成更安全的随机数
  296. const array = new Uint32Array(length)
  297. crypto.getRandomValues(array)
  298. let result = ''
  299. for (let i = 0; i < length; i++) {
  300. result += chars[array[i] % chars.length]
  301. }
  302. return result
  303. } else {
  304. // 回退方案
  305. let result = ''
  306. for (let i = 0; i < length; i++) {
  307. result += chars.charAt(Math.floor(Math.random() * chars.length))
  308. }
  309. return result
  310. }
  311. }
  312. export default {
  313. enhancedEncrypt,
  314. enhancedDecrypt,
  315. encryptSensitiveData,
  316. decryptLoginData,
  317. validateEncryptedData,
  318. generateSecureKey
  319. }