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>...</xml>,返回的是一段字符串文本,需要自己包装,最后回复给用户的内容是 <xml><ToUserName><![CDATA[toUser]]</ToUserName> <Encrypt><![CDATA[ 加密方法返回的字符串文本 ]]</Encrypt> </xml>,解密的方法则相反,传入的是加密字符串文本,返回的是加密前的 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;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文主要介绍移动端的加解密算法的分类、其优缺点特性及应用,帮助读者由浅入深地了解和选择加解密算法。文中会包含算法的...
    苹果粉阅读 11,572评论 5 29
  • 这篇文章主要讲述在Mobile BI(移动商务智能)开发过程中,在网络通信、数据存储、登录验证这几个方面涉及的加密...
    雨_树阅读 2,664评论 0 6
  • 之前的项目中接触过一些加密的方法,也没有太仔细的进行记录和研究。最近在写SDK时,加密模块的占比相当之大;借此时机...
    大雄記阅读 11,013评论 20 63
  • 最近,看了一部电影,《白日梦想家》。 《白日梦想家》是由本·斯蒂勒自导自演,《当幸福来敲门》的编剧史蒂夫·...
    生活旅行家东子阅读 579评论 0 0
  • 初步描绘,等到彩色铅笔到手后,继续修改。我觉得世界应该有色彩,而不是单一的黑白。期待我的下一步吧!
    史啸雲歪阅读 370评论 0 1