H5 微信小游戏群 openGID 解密

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/linshuhe1/article/details/82225008

这里使用的 Wechat Lib 版本是 2.0.6 ,高于此版本则不再能获得分享成功与否的回调。后来直接把基础库升级到 2.2.2 ,发现只是开发者工具拿不到回调而已,手机上还是还能拿到回调。

在使用微信小游戏的分享功能时,假如在 wx.showShareMenu 中设置了 withShareTicket: true

wx.showShareMenu({
    withShareTicket: true   // 分享到群可识别
});

那么,在分享成功回调中,假如分享到群则会得到一个 shareTickets 的字符串列表(列表长度与同时分享到的群数量相等,由于现在不能同时分享到多个群,所以这里列表长度是 1 ),假如分享到个人则无此项数据:

wx.shareAppMessage({
    title: '分享标题',
    imageUrl: imgUrl,
    query: data.query ? data.query : '',
    success: function (res) {
        // 分享成功
        console.log('---------- 分享成功')
        if (res.shareTickets) {
            
        }
    },
    fail: function (res) {
        // 分享失败
        console.log('---------- 分享失败')
    }
})

然而, shareTickets 列表中的字符串并非是群的唯一标识,而是一个获取群加密数据的 key,即使是同一个群每次分享得到的字符串也是不同的,想要获得群的唯一标识 OpenGId ,需要如下操作:

  • 通过 shareTicketwx.getShareInfo 接口得到加密数据:encryptedDataiv

  • 解密数据需要参考 加密数据解密算法 ,使用从 wx.login 获得的用户 session_key 和上述获得的 encryptedDataiv 这三个数据,然后使用 AES-128-CBC 算法(PKCS#7填充)来解密数据。

 

关于 AES 算法

网上有大把的文章讲解的,这里就不做赘述了。

 

实现步骤

首先,参考微信官方给出的多种语言( C++ 、 Node 、PHP 和 Python )的 demo ,当然唯一有参考价值的就是 Node 版本的,其实只有两个脚本:

测试脚本

// demo.js
var WXBizDataCrypt = require('./WXBizDataCrypt')
​
var appId = 'wx4f4bc4dec97d474b'
var sessionKey = 'tiihtNczf5v6AKRyjwEUhQ=='
var encryptedData = 'CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS'+
    '9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142d'+
    'NCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6'+
    '/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn'+
    '/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4P'+
    'C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns'+
    '/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV'+
    'oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuy'+
    'Db/XcxxmK01EpqOyuxINew=='
var iv = 'r7BXXKkLb8qrSNn05n0qiA=='
​
var pc = new WXBizDataCrypt(appId, sessionKey)
var data = pc.decryptData(encryptedData , iv)
console.log('解密后 data: ', data)

工具脚本

// WXBizDataCrypt.js
var crypto = require('crypto')
​
function WXBizDataCrypt(appId, sessionKey) {
  this.appId = appId
  this.sessionKey = sessionKey
}
​
WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) {
  // base64 decode
  var sessionKey = new Buffer(this.sessionKey, 'base64')
  encryptedData = new Buffer(encryptedData, 'base64')
  iv = new Buffer(iv, 'base64')
​
  try {
     // 解密
    var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
    // 设置自动 padding 为 true,删除填充补位
    decipher.setAutoPadding(true)
    var decoded = decipher.update(encryptedData, 'binary', 'utf8')
    decoded += decipher.final('utf8')
    
    decoded = JSON.parse(decoded)
​
  } catch (err) {
    throw new Error('Illegal Buffer')
  }
​
  if (decoded.watermark.appid !== this.appId) {
    throw new Error('Illegal Buffer')
  }
​
  return decoded
}
​
module.exports = WXBizDataCrypt

想要让 demo 运行起来也不难,直接初始化 node 工程结构:

$ cd Node
$ npm init  # 入口脚本设置为 demo.js
$ npm i crypto
$ node .    # 假如 npm init 时没有指定启动脚本为 demo.js , 则这里需要执行 node demo.js

结果发现,这里使用的 crypto 库是 Node 內建的库,假如要用于网页或微信小游戏上,需要进行改造构建自己的库。

 

自建库

由于微信小游戏中只需要使用到数据解码的接口,我们就只封装解码接口到库中,具体步骤如下:

  • 创建库脚本 index.js

    var crypto = require('crypto')
    ​
    module.exports = function DecryptWXData(_encryptedData, _iv, _sessionKey) {
      // base64 decode
      var sessionKey = new Buffer(_sessionKey, 'base64')
      var encryptedData = new Buffer(_encryptedData, 'base64')
      var iv = new Buffer(_iv, 'base64')
    ​
      try {
         // 解密
        var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
        // 设置自动 padding 为 true,删除填充补位
        decipher.setAutoPadding(true)
        var decoded = decipher.update(encryptedData, 'binary', 'utf8')
        decoded += decipher.final('utf8')
        
        decoded = JSON.parse(decoded)
    ​
      } catch (err) {
        throw new Error('Illegal Buffer')
      }
    ​
      return decoded
    }

    DecryptWXData 方法设置为模块的导出接口。

  • 然后,安装 browserify 工具和 gulify-js 工具:

    $ npm i -g browserify
    $ npm i -g uglify-js

    browserify 是一个让浏览器可以加载使用 Node 模块的打包工具,本质上是会将改模块及其依赖的模块代码都打包到脚本中,而不再依赖外部代码,由于包含很多兼容不同浏览器的代码,因此打出来的库会比较到。

    uglify-js 是用来压缩 js 脚本生成 .min.js 库脚本的工具。

  • 生成库脚本:

    $ browserify index.js -s DecryptWXData > decrypt.js
    $ uglify decrypt.js > decrypt.min.js

    这里 -s DecryptWXData 是指定一个单例给外部访问(为了兼容微信小游戏),当然也不能直接使用,需要做一些小调整,将最终接口赋给 window.DecryptWXData

发小最终压缩之后的库还要 413KB ,有点大,看来也只是作为一个临时方案,后面有时间再改用其他库。

 

引入库

将上面生成到库复制到工程的 libs/decrypt 目录下,创建对应的 .d.ts 提示脚本:

// decrypt.d.ts
declare function DecryptWXData(_encryptedData: string, _iv: string, _sessionKey: string);

然后在 egretProperties.json 中添加新库的配置:

{
    "name": "decrypt",
    "path": "./libs/decrypt"
}

直接当做全局方法来访问接口,使用如下:

let decryptedStr = DecryptWXData(encryptedData, iv, session_key);

 

其他

测试过程中可以借助在线工作验证输入输出结果是否正确:在线AES加解密 ,但是后来发现针对微信小游戏相关的数据进行解析,解密得到的都是空内容。

 

遇到的问题

我遇到了与 微信小游戏官方文档中的“加密数据解密算法”具体应该怎么实现 此贴一样的问题,即解密后的数据是一个 {"words":[],"sigBytes":0} ,最终发现原来使用库并不支持微信数据的解析。也可以使用 使用CryptoJS解决微信小程序用户信息解密 中的库,只是要自己将其打包到一个脚本中,而且需要自己做小游戏的适配:JS基础很烂的情况下找的微信小游戏解密方案

 

参考

 

阅读更多

没有更多推荐了,返回首页