Просмотр исходного кода

通用接口参数AES加密改为动态iv,解密工具适配调整

cfort 11 месяцев назад
Родитель
Сommit
456849a9ef

+ 1 - 1
src/business/platform/form/utils/custom/joinCURD.js

@@ -45,7 +45,7 @@ const dealData = (args, type) => {
     const data = typeof args === 'object' ? replaceNullWithEmpty(args) : args
     const plaintext = SHOW_PLAINTEXT ? { plaintext: data } : {}
     const res = {
-        ciphertext: encryptByAes(data),
+        ...encryptByAes(data),
         ...plaintext
     }
     return JSON.stringify(res)

+ 76 - 28
src/utils/encrypt.js

@@ -1,45 +1,93 @@
 import CryptoJS from 'crypto-js'
 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')
+// 固定IV 用于密码、get请求参数加密
+const defaultIv = CryptoJS.enc.Utf8.parse('5lDsNRe&UduJ97uS')
+
+// 动态IV生成器
+export const generateDynamicIV = () => {
+    // 1. 获取当前时间戳(8字节,64位)
+    const timestamp = Date.now()
+    // 将时间戳转换为8字节的WordArray(大端序)
+    const timestampBytes = CryptoJS.enc.Hex.parse(
+        timestamp.toString(16).padStart(16, '0')
+    )
+
+    // 2. 生成8字节随机数
+    const randomBytes = CryptoJS.lib.WordArray.random(8)
+
+    // 3. 构造IV(16字节)
+    const ivWords = [
+        // 前4字节:随机数的高4字节
+        randomBytes.words[0],
+        // 接下来4字节:时间戳的高4字节
+        timestampBytes.words[0],
+        // 接下来4字节:随机数的低4字节
+        randomBytes.words[1],
+        // 最后4字节:时间戳的低4字节
+        timestampBytes.words[1]
+    ]
+
+    return CryptoJS.lib.WordArray.create(ivWords, 16)
+}
 
 // AES加密
-export const encryptByAes = (pwd, type = 'normal') => {
+export const encryptByAes = (params, type = 'normal') => {
     let encrypted = ''
     const key = type === 'normal' ? key1 : key2
-    const iv = type === 'normal' ? iv1 : iv2
-    if (typeof pwd === 'string') {
-        const srcs = CryptoJS.enc.Utf8.parse(pwd)
-        const options = {
-            iv: iv,
-            mode: CryptoJS.mode.CBC, // 使用CBC模式
-            padding: CryptoJS.pad.Pkcs7 // 使用PKCS7填充
-        }
-        encrypted = CryptoJS.AES.encrypt(srcs, key, options)
-    } else if (typeof pwd === 'object') {
-        const data = JSON.stringify(pwd)
+    const iv = type === 'normal' ? generateDynamicIV() : defaultIv
+    if (typeof params === 'string' || typeof params === 'object') {
+        const data = typeof params === 'string' ? params : JSON.stringify(params)
         const srcs = CryptoJS.enc.Utf8.parse(data)
-        const options = {
+        encrypted = CryptoJS.AES.encrypt(srcs, key, {
             iv: iv,
-            mode: CryptoJS.mode.CBC, // 使用CBC模式
-            padding: CryptoJS.pad.Pkcs7 // 使用PKCS7填充
-        }
-        encrypted = CryptoJS.AES.encrypt(srcs, key, options)
+            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)
+}
+
+const isValidBase64 = (str) => {
+    try {
+        return btoa(atob(str)) === str
+    } catch (e) {
+        return false
     }
-    return encrypted.ciphertext.toString(CryptoJS.enc.Base64)
 }
 
-export const decryptByAes = (encryptedText, type = 'normal') => {
+export const decryptByAes = (encryptedText, ivBase64 = null, type = 'normal') => {
     const key = type === 'normal' ? key1 : key2
-    const iv = type === 'normal' ? iv1 : iv2
+
+    if (!isValidBase64(ivBase64)) {
+        console.error('ivBase64不是有效的 Base64 字符串')
+        return ''
+    }
+    // 解析动态IV
+    const iv = CryptoJS.enc.Base64.parse(ivBase64)
+
     const options = {
         iv: iv,
-        mode: CryptoJS.mode.CBC, // 使用CBC模式
-        padding: CryptoJS.pad.Pkcs7 // 使用PKCS7填充
+        mode: CryptoJS.mode.CBC,
+        padding: CryptoJS.pad.Pkcs7
+    }
+
+    try {
+        const encryptedData = CryptoJS.enc.Base64.parse(encryptedText)
+        const decryptedData = CryptoJS.AES.decrypt(
+            { ciphertext: encryptedData },
+            key,
+            options
+        )
+
+        // 返回UTF-8解码的明文(去除首尾空格)
+        return decryptedData.toString(CryptoJS.enc.Utf8).trim()
+    } catch (error) {
+        console.error('AES解密失败:', error)
+        return ''
     }
-    const encryptedData = CryptoJS.enc.Base64.parse(encryptedText)
-    const decryptedData = CryptoJS.AES.decrypt({ ciphertext: encryptedData }, key, options)
-    const decryptedText = decryptedData.toString(CryptoJS.enc.Utf8)
-    return decryptedText.trim()
 }

+ 2 - 2
src/utils/request.js

@@ -97,7 +97,7 @@ service.interceptors.request.use(async config => {
     if (config.method.toUpperCase() === 'GET') {
         if (ENCRYPT_GET_PARAMS) {
             config.params = {
-                _p: Utils.isNotEmpty(config.params) ? encryptByAes(JSON.stringify(config.params)) : undefined,
+                _p: Utils.isNotEmpty(config.params) ? encryptByAes(JSON.stringify(config.params), 'get') : undefined,
                 _t: new Ids([32, 36, 1]).next()
             }
         } else {
@@ -277,4 +277,4 @@ service.interceptors.response.use(response => {
     return Promise.reject(error)
 })
 
-export default service
+export default service

+ 16 - 1
src/views/platform/system/tools/convertByAes.vue

@@ -55,6 +55,17 @@
                         />
                     </div>
                 </div>
+                <div class="item">
+                    <div class="label">ivBase64</div>
+                    <div class="value">
+                        <el-input
+                            v-model="ivBase64"
+                            type="textarea"
+                            :rows="1"
+                            placeholder="请输入ivBase64"
+                        />
+                    </div>
+                </div>
             </div>
         </div>
     </el-dialog>
@@ -72,6 +83,7 @@ export default {
         return {
             dialogVisible: this.visible,
             plaintext: '',
+            ivBase64: '',
             ciphertext: ''
         }
     },
@@ -102,7 +114,10 @@ export default {
             if (!this.ciphertext) {
                 return this.$message.warning('请输入密文!')
             }
-            const temp = this.$common.decryptByAes(this.ciphertext)
+            if (!this.ivBase64) {
+                return this.$message.warning('请输入ivBase64!')
+            }
+            const temp = this.$common.decryptByAes(this.ciphertext, this.ivBase64)
             if (this.$utils.isEmpty(temp)) {
                 return this.$message.error('密文无效,无法解密!')
             }