- Auth module: WeChat OAuth2 scan-to-login flow with terminal QR code - Token persistence to ~/.openclaw/wechat-access-auth.json (chmod 600) - Token resolution: config > saved state > interactive login - Invite code verification (configurable bypass) - Production/test environment support - AGP WebSocket client with heartbeat, reconnect, wake detection - Message handler: Agent dispatch with streaming text and tool calls - Random device GUID generation (persisted, no real machine ID)
97 lines
3.6 KiB
TypeScript
97 lines
3.6 KiB
TypeScript
// ============================================
|
||
// 加密解密工具
|
||
// ============================================
|
||
// 处理微信服务号的消息加密、解密和签名验证
|
||
// 微信使用 AES-256-CBC 加密算法和 SHA-1 签名算法
|
||
|
||
/**
|
||
* 验证签名参数接口
|
||
* @property token - 微信服务号配置的 Token
|
||
* @property timestamp - 时间戳
|
||
* @property nonce - 随机数
|
||
* @property encrypt - 加密的消息内容
|
||
* @property signature - 微信生成的签名,用于验证消息来源
|
||
*/
|
||
export interface VerifySignatureParams {
|
||
token: string;
|
||
timestamp: string;
|
||
nonce: string;
|
||
encrypt: string;
|
||
signature: string;
|
||
}
|
||
|
||
/**
|
||
* 解密消息参数接口
|
||
* @property encodingAESKey - 微信服务号配置的 EncodingAESKey(43位字符)
|
||
* @property receiveId - 接收方 ID(通常是服务号的原始 ID)
|
||
* @property encrypt - 加密的消息内容(Base64 编码)
|
||
*/
|
||
export interface DecryptMessageParams {
|
||
encodingAESKey: string;
|
||
receiveId: string;
|
||
encrypt: string;
|
||
}
|
||
|
||
/**
|
||
* 验证微信消息签名
|
||
* @param params - 签名验证参数
|
||
* @returns 签名是否有效
|
||
* @description
|
||
* 验证流程:
|
||
* 1. 将 token、timestamp、nonce、encrypt 按字典序排序
|
||
* 2. 拼接成字符串
|
||
* 3. 进行 SHA-1 哈希
|
||
* 4. 与微信提供的 signature 比对
|
||
*
|
||
* **注意:当前为简化实现,生产环境需要实现真实的 SHA-1 签名验证**
|
||
*/
|
||
export const verifySignature = (params: VerifySignatureParams): boolean => {
|
||
// TODO: 实现真实的签名验证逻辑
|
||
// 参考算法:
|
||
// const arr = [params.token, params.timestamp, params.nonce, params.encrypt].sort();
|
||
// const str = arr.join('');
|
||
// const hash = crypto.createHash('sha1').update(str).digest('hex');
|
||
// return hash === params.signature;
|
||
|
||
console.log("[wechat-access] 验证签名参数:", params);
|
||
return true; // 简化实现,直接返回 true
|
||
};
|
||
|
||
/**
|
||
* 解密微信消息
|
||
* @param params - 解密参数
|
||
* @returns 解密后的明文消息(JSON 字符串)
|
||
* @description
|
||
* 解密流程:
|
||
* 1. 将 Base64 编码的 encrypt 解码为二进制
|
||
* 2. 使用 AES-256-CBC 算法解密(密钥由 encodingAESKey 派生)
|
||
* 3. 去除填充(PKCS7)
|
||
* 4. 提取消息内容(格式:随机16字节 + 4字节消息长度 + 消息内容 + receiveId)
|
||
* 5. 验证 receiveId 是否匹配
|
||
*
|
||
* **注意:当前为简化实现,返回模拟数据,生产环境需要实现真实的 AES 解密**
|
||
*/
|
||
export const decryptMessage = (params: DecryptMessageParams): string => {
|
||
// TODO: 实现真实的解密逻辑
|
||
// 参考算法:
|
||
// const key = Buffer.from(params.encodingAESKey + '=', 'base64');
|
||
// const iv = key.slice(0, 16);
|
||
// const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||
// decipher.setAutoPadding(false);
|
||
// let decrypted = Buffer.concat([decipher.update(params.encrypt, 'base64'), decipher.final()]);
|
||
// // 去除 PKCS7 填充
|
||
// const pad = decrypted[decrypted.length - 1];
|
||
// decrypted = decrypted.slice(0, decrypted.length - pad);
|
||
// // 提取消息内容
|
||
// const content = decrypted.slice(16);
|
||
// const msgLen = content.readUInt32BE(0);
|
||
// const message = content.slice(4, 4 + msgLen).toString('utf8');
|
||
// const receiveId = content.slice(4 + msgLen).toString('utf8');
|
||
// if (receiveId !== params.receiveId) throw new Error('receiveId mismatch');
|
||
// return message;
|
||
|
||
console.log("[wechat-access] 解密参数:", params);
|
||
// 返回模拟的解密结果(标准微信消息格式)
|
||
return '{"msgtype":"text","Content":"Hello from 服务号","MsgId":"123456","FromUserName":"user001","ToUserName":"gh_test","CreateTime":1234567890}';
|
||
};
|