NodeJS开发微信记录(1)---安全模式AES加解密

总算通过了,在网上找了很多人的实现方法,都有这样那样的问题。本人对加密完全是小白一个,微信官方的加密解密技术方案看了很久才勉强弄明白,总结一下需要注意的地方。

  1. 数据使用 PKCS#7填充,填充规则看文档。通俗一点的解释是你用来加密的 buf 必须是 32 位的整数倍,如果加密前的buf长度不是整数倍怎么办呢?就要给它补上,补 (32 - N%32)(32-N%32), 比如你长度是 30,后面就要2个02,29就是补3个 03。这里有个问题是 crypto 模块默认是自动填充的,而且填充的内容规则不同,需要自己来实现填充规则,而且要设置 setAutoPadding(false)。下面代码有实现,可以参考。
  2. 解密格式为 'aes-256-cbc' ,官方说法是‘AES采用CBC模式,秘钥长度为32个字节(256位)’,琢磨了很久。用通俗一点的说法是,
  3. 下面代码中,消息加密的方法,传入的参数是明文回复的消息文本 ...,返回的是一段字符串文本,需要自己包装,最后回复给用户的内容是 ,解密的方法则相反,传入的是加密字符串文本,返回的是加密前的 xml 消息文本,和官方文档中的消息格式说明是一样的。
'use strict'
/**
 * WxCrypt 微信消息加解密的方法
 * @return new WxCrypt(wxConfig)
 * 返回一个单例即可,不需要重复引用
 */
const crypto = require('crypto');
const wxConfig = {
// 传入配置信息
  token: 'xxx', 
  appid: 'xxx',
  msg_signature: 'xxx',
  encodingAESKey: 'xxx'
}
class WxCrypt {
    constructor(opts){
        //初始化需要用到的属性
        this.token = opts.token;
        this.appid = opts.appid;
        this.msg_signature = opts.msg_signature; //query 传进来签名
        this.aesKey = new Buffer(opts.encodingAESKey + '=', 'base64');
        this.IV = this.aesKey.slice(0, 16)
    }
    encrypt (xmlMsg) {
        /*
         *@params String xmlMsg 格式化后的 xml 字符串
         *@return String 加密后的字符串 填入到 Encrypt 节点中
         * 参照官方文档 需要返回一个buf: 随机16字节 + xmlMsg.length(4字节)+xmlMsg+appid。
         * buf的字节长度需要填充到 32的整数,填充长度为 32-buf.length%32, 每一个字节为 32-buf.length%32
         */
        let random16 = crypto.pseudoRandomBytes(16);
        let msg = new Buffer(xmlMsg);
        let msgLength = new Buffer(4);
        msgLength.writeUInt32BE(msg.length, 0);

        let corpId = new Buffer(this.appid);

        let raw_msg = Buffer.concat([random16,msgLength,msg ,corpId]);
        let cipher = crypto.createCipheriv('aes-256-cbc', this.aesKey, this.IV);
        cipher.setAutoPadding(false);//重要,autopadding填充的内容无法正常解密
       raw_msg = this.PKCS7Encode(raw_msg);

        let cipheredMsg = Buffer.concat([cipher.update(/*encoded*/raw_msg), cipher.final()]);

        return cipheredMsg.toString('base64');   
    }

    decrypt(text) {
          /*
           *@params String text 需要解密的字段(Encrypt节点中的内容)
           * @return String msg_content 返回消息内容(xml字符串)
           */
        
          let plain_text;
          let decipher = crypto.Decipheriv('aes-256-cbc', this.aesKey, this.IV)
// crypto.Decipheriv == crypto.createDecipheriv 两个方法是一样的
           decipher.setAutoPadding(false);//重要
            
          let decipheredBuff = Buffer.concat([decipher.update(text, 'base64'), decipher.final()])
          decipheredBuff = this.PKCS7Decode(decipheredBuff)

            
        let len_netOrder_corpid = decipheredBuff.slice(16) 
        //切割掉16个随机字符,剩余为 (4字节的 msg_len) + msg_content(长度为 msg_len ) + msg_appId 
        let msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0)
        let msg_content = len_netOrder_corpid.slice(4, msg_len+4).toString('utf-8')
      //  let msg_appId =len_netOrder_corpid.slice(msg_len+4).toString('utf-8')
    
        return msg_content
    }
    PKCS7Decode(buff) {
        /*
         *去除尾部自动填充的内容
         */
        let padContent = buff[length -1]
            if(padContent<1 || padContent >32){
                    padContent = 0
            }
        let padLen = padContent;//根据填充规则,填充长度 = 填充内容,这一步赋值可以省略
        return buff.slice(0, buff.length - padLen)
    }
    PKCS7Encode(buff) {
      let blockSize = 32;
      let needPadLen = 32 - buff.length % 32
      if( needPadLen == 0) {
          needPadLen = blockSize
      }
      let pad = new Buffer(needPadLen)
      pad.fill(needPadLen)
      let newBuff = Buffer.concat([buff, pad])
      return newBuff
    }
 }
var wxCrypt = new WxCrypt(wxConfig);
module.exports = wxCrypt;

你可能感兴趣的:(NodeJS开发微信记录(1)---安全模式AES加解密)