import GameConfigModel from "../store/models/gameConfig.model"; import { generateRandomIV, sortObj } from "./helper"; import md5 from 'md5'; import CryptoJS from 'crypto-js'; /** * 生成签名 * @param params * @returns */ export const generateSign = (params: { time: number; [key: string]: any; }) => { const gemeConfig = GameConfigModel.getInstance().get() params.time = Math.floor(new Date().getTime() / 1000); let newData = sortObj(params); let signStr = ""; for (let k in newData) { if (k === "sign") { continue; } if (newData[k] === "") { continue; } if (newData[k] === undefined) { continue; } if (newData[k] === null) { continue; } signStr = signStr + k + "=" + newData[k] + "&"; } signStr = signStr.substring(0, signStr.length - 1) + "&secret=" + gemeConfig.mini_program_id; // console.log("加密原串:" + signStr); params.sign = md5(signStr); let newParams: any = {} newParams.encrypt_data = encryptData(params); return newParams; } /** * 加密数据 * @param params * @returns */ export const encryptData = (params: any) => { console.log('加密前',params) const key = import.meta.env.SDK_VITE_KEY; // 将参数对象转换为 JSON 字符串 const data = JSON.stringify(params); // 生成 16 字节的随机 IV (AES-256-CBC 需要 16 字节 IV) const iv = generateRandomIV(16); // 将密钥转换为 WordArray // AES-256 需要 32 字节 (256 位) 的密钥 // 如果 key 长度不够,使用 MD5 哈希来生成 32 字节的密钥 let keyWordArray; if (key.length === 32) { // 如果 key 正好是 32 字节,直接使用 UTF8 解析 keyWordArray = CryptoJS.enc.Utf8.parse(key); } else { // 否则使用 MD5 哈希生成 32 字节的密钥(MD5 生成 16 字节,但 crypto-js 会处理) // 对于 AES-256,我们需要 32 字节,可以使用 SHA256 或者重复 MD5 keyWordArray = CryptoJS.SHA256(key); } // 使用 AES-256-CBC 加密,返回原始二进制数据 const encrypted = CryptoJS.AES.encrypt(data, keyWordArray, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 将 IV 和密文拼接(都是 WordArray,直接拼接) // PHP 中:$iv . $encrypted (直接拼接二进制数据) const combined = iv.concat(encrypted.ciphertext); // 将拼接后的数据转换为 Base64 编码 // PHP 中:base64_encode($iv . $encrypted) const base64Encoded = CryptoJS.enc.Base64.stringify(combined); // 返回加密后的数据,将原始参数替换为加密数据 return base64Encoded; } /** * 解密数据 * @param encryptedData * @returns */ export const decryptData = (encryptedData: string) => { const key = import.meta.env.SDK_VITE_KEY; try { // 如果数据为空或无效,直接返回 if (!encryptedData || typeof encryptedData !== 'string') { return encryptedData; } // Base64 解码 const combined = CryptoJS.enc.Base64.parse(encryptedData); // 提取前 16 字节作为 IV(AES-256-CBC 需要 16 字节 IV) const iv = CryptoJS.lib.WordArray.create(combined.words.slice(0, 4), 16); // 提取剩余部分作为密文 const ciphertext = CryptoJS.lib.WordArray.create(combined.words.slice(4), combined.sigBytes - 16); // 将密钥转换为 WordArray(与加密方法保持一致) let keyWordArray; if (key && key.length === 32) { // 如果 key 正好是 32 字节,直接使用 UTF8 解析 keyWordArray = CryptoJS.enc.Utf8.parse(key); } else if (key) { // 否则使用 SHA256 哈希生成 32 字节的密钥 keyWordArray = CryptoJS.SHA256(key); } else { throw new Error('解密密钥不能为空'); } // 创建 CipherParams 对象用于解密 const cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: ciphertext }); // 使用 AES-256-CBC 解密 const decrypted = CryptoJS.AES.decrypt(cipherParams, keyWordArray, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 将解密后的数据转换为 UTF8 字符串 const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8); console.log("解密后的数据:", JSON.parse(decryptedStr)); // 解析 JSON 字符串并返回对象 return JSON.parse(decryptedStr); } catch (error) { console.error('解密失败:', error); // 如果解密失败,返回原始数据 return encryptedData; } }