UNPKG

@tnwx/commons

Version:

TTNWX 微信系开发脚手架之公共模块

145 lines 5.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const crypto = require("crypto"); const xml2js = require("xml2js"); class CryptoKit { constructor(config, msgSignature, timestamp, nonce) { this.aesModel = 'aes-256-cbc'; this.token = config.getToken; this.appId = config.getAppId; this.encodingAesKey = Buffer.from(config.getEncodingAesKey + '=', 'base64'); this.iv = this.encodingAesKey.slice(0, 16); this.msgSignature = msgSignature; this.timestamp = timestamp; this.nonce = nonce; } getMsgSignature(encryptedMsg) { //将token、timestamp、nonce、cryptedMsg 四个参数进行字典序排序,并拼接成一个字符串 let tempStr = [this.token, this.timestamp, this.nonce, encryptedMsg].sort().join(''); //创建加密类型 const hashCode = crypto.createHash('sha1'); //对传入的字符串进行加密 let tempSignature = hashCode.update(tempStr, 'utf8').digest('hex'); //将 sha1 加密的签名字符串返回 return tempSignature; } decrypt(echostr) { // 实例 AES 解密对象 let deCipheriv = crypto.createDecipheriv(this.aesModel, this.encodingAesKey, this.iv); // 设置自定填充数据为 false deCipheriv.setAutoPadding(false); // 对密文解密对密文解密 并去除前 16 个随机字符串 let decipheredBuff = Buffer.concat([deCipheriv.update(echostr, 'base64'), deCipheriv.final()]); decipheredBuff = this.PKCS7Decoder(decipheredBuff); // 移除头部16个随机字节 let len_netOrder_corpid = decipheredBuff.slice(16); // 4个字节的 msg_len let msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0); // 截取 msg_len 长度的 msg let result = len_netOrder_corpid.slice(4, msg_len + 4).toString(); return result; } /** * 微信消息解密 * @param encryptMsg 加密字符串 */ decryptMsg(encryptMsg) { //获取签名认证 let tempSignature = this.getMsgSignature(encryptMsg); //判断消息是否来自微信服务器 if (this.msgSignature !== tempSignature) { throw new Error('msgSignature is not invalid'); } //实例 AES 解密对象 let deCipheriv = crypto.createDecipheriv(this.aesModel, this.encodingAesKey, this.iv); //设置自定填充数据为 false deCipheriv.setAutoPadding(false); //对密文解密 并去除前 16 个随机字符串 let deEncryptedMsg = Buffer.concat([deCipheriv.update(encryptMsg, 'base64'), deCipheriv.final()]).toString('utf8'); //获取填充字符串的位置 let pad = deEncryptedMsg.charCodeAt(deEncryptedMsg.length - 1); //对微信消息进行处理 deEncryptedMsg = deEncryptedMsg.slice(20, -pad).replace(/<\/xml>.*/, '</xml>'); //讲解密后的XML 转为 JSON 对象 return this.parseXmlToObj(deEncryptedMsg); } /** * 微信消息加密 * @param xmlMsg */ encryptMsg(xmlMsg) { //声明 16位的随机字符串 let random = crypto.randomBytes(8).toString('hex'); let text = Buffer.from(xmlMsg); let buf = Buffer.alloc(4); buf.writeUInt32BE(text.length, 0); //进行PKCS7补位 let pack = this.KCS7Encoder(20 + text.length + this.appId.length); //拼接要加密的字符串 let content = random + buf.toString('binary') + text.toString('binary') + this.appId + pack; //实例 AES 加密对象 let cipheriv = crypto.createCipheriv(this.aesModel, this.encodingAesKey, this.iv); //设置自定填充数据为 false cipheriv.setAutoPadding(false); //对明文加密 let encryptedMsg = Buffer.concat([cipheriv.update(content, 'binary'), cipheriv.final()]).toString('base64'); //获取认证签名 let msgSignature = this.getMsgSignature(encryptedMsg); //返回XML结果 return CryptoKit.xmlParser.buildObject({ Encrypt: encryptedMsg, MsgSignature: msgSignature, TimeStamp: this.timestamp, Nonce: this.nonce }); } KCS7Encoder(textLength) { let blockSize = 32; // 计算需要填充的位数 let amountToPad = blockSize - (textLength % blockSize); if (amountToPad === 0) { amountToPad = blockSize; } // 获得补位所用的字符 let pad = String.fromCharCode(amountToPad), s = []; for (let i = 0; i < amountToPad; i++) s.push(pad); return s.join(''); } PKCS7Decoder(buff) { var pad = buff[buff.length - 1]; if (pad < 1 || pad > 32) { pad = 0; } return buff.slice(0, buff.length - pad); } parseXmlToObj(xml) { if (!xml || typeof xml != 'string') return {}; let re = {}; xml = xml.replace(/^<xml>|<\/xml>$/g, ''); let ms = xml.match(/<([a-z0-9]+)>([\s\S]*?)<\/\1>/gi); if (ms && ms.length > 0) { ms.forEach(t => { let ms = t.match(/<([a-z0-9]+)>([\s\S]*?)<\/\1>/i) || []; let tagName = ms[1]; let cdata = ms[2] || ''; cdata = cdata.replace(/^\s*<\!\[CDATA\[\s*|\s*\]\]>\s*$/g, ''); re[tagName] = cdata; }); } return re; } } exports.CryptoKit = CryptoKit; //用于构建 xml 结构 CryptoKit.xmlParser = new xml2js.Builder({ rootName: 'xml', cdata: true, headless: true, renderOpts: { pretty: true, indent: ' ' } }); //# sourceMappingURL=CryptoKit.js.map