chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
@@ -0,0 +1,90 @@
layui.define(function (exports) {
"use strict";
class WeChatJsonCrypto {
constructor(token, encodingAesKey, appId) {
if (!token || !appId || encodingAesKey.length !== 43 || !/^[A-Za-z0-9+\/]{43}$/.test(encodingAesKey)) {
throw new Error("配置错误:Token/AppID不能为空,EncodingAESKey需为43位标准Base64字符");
}
this.token = token;
this.appId = appId;
this.blockSize = 32;
this.encodingAesKey = CryptoJS.enc.Base64.parse(encodingAesKey + '=');
if (!this.encodingAesKey.words?.length) throw new Error("密钥解析失败");
}
// ✅ 核心修复1:加密时直接传入明文WordArray(非{ ciphertext: ... }
async encrypt(data) {
const jsonBytes = new TextEncoder().encode(JSON.stringify(data, null, 0));
const appIdBytes = new TextEncoder().encode(this.appId);
const random = crypto.getRandomValues(new Uint8Array(16));
const lenBytes = new Uint8Array(new DataView(new ArrayBuffer(4)).buffer);
new DataView(lenBytes.buffer).setUint32(0, jsonBytes.length, false);
// 拼接明文: random(16) + len(4) + json + appId
const plain = new Uint8Array(20 + jsonBytes.length + appIdBytes.length);
plain.set(random); plain.set(lenBytes, 16);
plain.set(jsonBytes, 20); plain.set(appIdBytes, 20 + jsonBytes.length);
// PKCS#7填充
const pad = this.blockSize - (plain.length % this.blockSize);
const padded = new Uint8Array(plain.length + pad).fill(pad, plain.length);
padded.set(plain);
const iv = CryptoJS.lib.WordArray.create(this.encodingAesKey.words.slice(0, 4));
const encrypted = CryptoJS.AES.encrypt(
CryptoJS.lib.WordArray.create(padded), // ← 修复点:直接传明文
this.encodingAesKey,
{ iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding }
);
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}
async decrypt(encryptedBase64) {
try {
const cipherWA = CryptoJS.enc.Base64.parse(encryptedBase64);
if (cipherWA.sigBytes < 32) return null;
const iv = CryptoJS.lib.WordArray.create(this.encodingAesKey.words.slice(0, 4));
const decryptedWA = CryptoJS.AES.decrypt(
{ ciphertext: cipherWA },
this.encodingAesKey,
{ iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding }
);
// 转Uint8Array
const decrypted = new Uint8Array(decryptedWA.sigBytes);
for (let i = 0; i < decryptedWA.sigBytes; i++) {
decrypted[i] = (decryptedWA.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
}
// ✅ 核心修复2:严格PKCS#7填充验证与移除
const padLen = decrypted[decrypted.length - 1];
if (padLen < 1 || padLen > this.blockSize || decrypted.length < padLen) return null;
for (let i = decrypted.length - padLen; i < decrypted.length; i++) {
if (decrypted[i] !== padLen) return null;
}
const plain = decrypted.slice(0, -padLen); // 移除填充
if (plain.length < 20) return null;
const jsonLen = new DataView(plain.slice(16, 20).buffer).getUint32(0, false);
if (jsonLen <= 0 || 20 + jsonLen > plain.length) return null;
const jsonStr = new TextDecoder().decode(plain.slice(20, 20 + jsonLen));
// ✅ 核心修复3:AppID直接解码(无字符过滤)
const fromAppId = new TextDecoder().decode(plain.slice(20 + jsonLen));
if (fromAppId !== this.appId) return null;
return JSON.parse(jsonStr);
} catch {
return null;
}
}
generateSignature(timestamp, nonce, encryptMsg) {
return CryptoJS.SHA1([this.token, timestamp, nonce, encryptMsg].sort().join('')).toString(CryptoJS.enc.Hex);
}
}
// 导出模块
exports('JsonCrypto', WeChatJsonCrypto);
});