Przeglądaj źródła

通用接口参数AES加密增加动态派生密钥逻辑,解密工具适配调整

cfort 11 miesięcy temu
rodzic
commit
e9c1a1ae21
2 zmienionych plików z 104 dodań i 38 usunięć
  1. 102 37
      src/utils/encrypt.js
  2. 2 1
      src/utils/joinCURD.js

+ 102 - 37
src/utils/encrypt.js

@@ -1,12 +1,34 @@
 import CryptoJS from 'crypto-js'
+const key = '49PBou+TREIOzSHj'
 const key1 = CryptoJS.enc.Utf8.parse('dmngJmmO+9GMw+tu')
-// const iv1 = CryptoJS.enc.Utf8.parse('sanXyqhk8+U7LPP4')
-const key2 = CryptoJS.enc.Utf8.parse('49PBou+TREIOzSHj')
-// const iv2 = CryptoJS.enc.Utf8.parse('5lDsNRe&UduJ97uS')
-
+const key2 = CryptoJS.enc.Utf8.parse(key)
 // 固定IV 用于密码、get请求参数加密
 const defaultIv = CryptoJS.enc.Utf8.parse('5lDsNRe&UduJ97uS')
 
+// 密钥派生参数配置
+const PBKDF2_CONFIG = {
+  // 盐值长度(bits)
+  saltSize: 128,
+  // 迭代次数
+  iterations: 10000,
+  // 密钥长度(256bit)
+  keySize: 256 / 32,
+  hashAlgorithm: CryptoJS.algo.SHA256
+}
+
+// 动态密钥派生器
+const deriveDynamicKey = (token, salt) => {
+  if (!token || typeof token !== 'string') {
+    throw new Error('Invalid user token')
+  }
+
+  return CryptoJS.PBKDF2(token, salt, {
+    keySize: PBKDF2_CONFIG.keySize,
+    iterations: PBKDF2_CONFIG.iterations,
+    hasher: PBKDF2_CONFIG.hashAlgorithm
+  })
+}
+
 // 动态IV生成器
 export const generateDynamicIV = () => {
   // 1. 获取当前时间戳(8字节,64位)
@@ -34,25 +56,58 @@ export const generateDynamicIV = () => {
   return CryptoJS.lib.WordArray.create(ivWords, 16)
 }
 
-// AES加密
-export const encryptByAes = (params, type = 'normal') => {
+const xorEncrypt = (text) => {
   let encrypted = ''
-  const key = type === 'normal' ? key1 : key2
-  const iv = type === 'normal' ? generateDynamicIV() : defaultIv
-  if (typeof params === 'string' || typeof params === 'object') {
+  for (let i = 0; i < text.length; i++) {
+    const charCode = text.charCodeAt(i) ^ key.charCodeAt(i % key.length)
+    encrypted += String.fromCharCode(charCode)
+  }
+
+  return btoa(encrypted)
+}
+
+const xorDecrypt = (encryptedBase64) => {
+  // 从Base64解码
+  const decodedStr = atob(encryptedBase64)
+  let decrypted = ''
+
+  // 异或解密
+  for (let i = 0; i < decodedStr.length; i++) {
+    const charCode = decodedStr.charCodeAt(i) ^ key.charCodeAt(i % key.length)
+    decrypted += String.fromCharCode(charCode)
+  }
+
+  return decrypted
+}
+
+// AES加密
+export const encryptByAes = (params, type = 'dynamic', token) => {
+  try {
+    const isDynamic = type === 'dynamic'
+    // 生成随机盐值
+    const salt = CryptoJS.lib.WordArray.random(PBKDF2_CONFIG.saltSize)
+    // 动态派生密钥
+    const derivedKey = isDynamic ? deriveDynamicKey(token, salt) : key2
+    // 生成动态iv
+    const iv = isDynamic ? generateDynamicIV() : defaultIv
     const data = typeof params === 'string' ? params : JSON.stringify(params)
     const srcs = CryptoJS.enc.Utf8.parse(data)
-    encrypted = CryptoJS.AES.encrypt(srcs, key, {
+
+    const encrypted = CryptoJS.AES.encrypt(srcs, derivedKey, {
       iv: iv,
       mode: CryptoJS.mode.CBC,
       padding: CryptoJS.pad.Pkcs7
     })
-  }
 
-  return type === 'normal' ? {
-    ciphertext: encrypted.ciphertext.toString(CryptoJS.enc.Base64),
-    iv: iv.toString(CryptoJS.enc.Base64)
-  } : encrypted.ciphertext.toString(CryptoJS.enc.Base64)
+    if (isDynamic) {
+      const str = encrypted.ciphertext.toString(CryptoJS.enc.Base64) + '|' + iv.toString(CryptoJS.enc.Base64) + '|' + salt.toString(CryptoJS.enc.Base64) + '|' + token
+      return xorEncrypt(str)
+    }
+    return encrypted.ciphertext.toString(CryptoJS.enc.Base64)
+  } catch (error) {
+    console.error('Encryption error:', error)
+    throw new Error('Encryption failed')
+  }
 }
 
 const isValidBase64 = (str) => {
@@ -63,35 +118,45 @@ const isValidBase64 = (str) => {
   }
 }
 
-export const decryptByAes = (encryptedText, ivBase64 = null, type = 'normal') => {
-  const key = type === 'normal' ? key1 : key2
+export const decryptByAes = (ciphertext) => {
+  try {
+    // 初始化解密参数
+    let cipherToDecrypt = ciphertext
+    let iv = defaultIv
+    let key = key2
 
-  if (!isValidBase64(ivBase64)) {
-    console.error('ivBase64不是有效的 Base64 字符串')
-    return ''
-  }
-  // 解析动态IV
-  const iv = CryptoJS.enc.Base64.parse(ivBase64)
+    // 尝试XOR解密并解析参数
+    const decryptedStr = xorDecrypt(ciphertext)
+    const parts = decryptedStr.split('|')
+    if (parts.length === 4) {
+      const [ciphertextBase64, ivBase64, saltBase64, token] = parts
 
-  const options = {
-    iv: iv,
-    mode: CryptoJS.mode.CBC,
-    padding: CryptoJS.pad.Pkcs7
-  }
+      if (token && !(isValidBase64(ivBase64) && isValidBase64(saltBase64))) {
+        console.error('解密失败: 无效的IV参数或salt参数!')
+        return ''
+      }
+      // 转换编码
+      iv = CryptoJS.enc.Base64.parse(ivBase64)
+      const salt = CryptoJS.enc.Base64.parse(saltBase64)
+      // 动态派生密钥
+      key = deriveDynamicKey(token, salt)
+      cipherToDecrypt = ciphertextBase64
+    }
 
-  try {
-    const encryptedData = CryptoJS.enc.Base64.parse(encryptedText)
-    const decryptedData = CryptoJS.AES.decrypt(
-      { ciphertext: encryptedData },
-      key,
-      options
+    // 解密
+    const decrypted = CryptoJS.AES.decrypt({
+        ciphertext: CryptoJS.enc.Base64.parse(cipherToDecrypt)
+      },
+      key, {
+        iv,
+        mode: CryptoJS.mode.CBC,
+        padding: CryptoJS.pad.Pkcs7
+      }
     )
 
-    // 返回UTF-8解码的明文(去除首尾空格)
-    return decryptedData.toString(CryptoJS.enc.Utf8).trim()
+    return decrypted.toString(CryptoJS.enc.Utf8).trim()
   } catch (error) {
     console.error('AES解密失败:', error)
     return ''
   }
 }
-

+ 2 - 1
src/utils/joinCURD.js

@@ -3,6 +3,7 @@ import { normal } from './requestType'
 import { encryptByAes } from './encrypt'
 import { mapValues } from 'lodash'
 import { SHOW_PLAINTEXT } from '@/constant'
+import { getToken } from '@/utils/auth'
 
 // 请求方式默认POST
 const post = (type, data, method = 'post', loading = false) => {
@@ -46,7 +47,7 @@ const dealData = (args, type) => {
   const data = typeof args === 'object' ? replaceNullWithEmpty(args) : args
   const plaintext = SHOW_PLAINTEXT ? { plaintext: data } : {}
   const res = {
-    ...encryptByAes(data),
+    ciphertext: encryptByAes(data, 'dynamic', getToken()),
     ...plaintext
   }
   return JSON.stringify(res)