微信小程序中获取用户手机号密文数据解密报错问题

一、问题描述

微信小程序获取手机号,官方通常会返回密文数据给我们,此时就需要我们自行解密数据。在揭秘的数据过程中会发现,第一次授权获取手机号会出现错误,再次获取的时候就能够正常获取。
错误信息一般分两种:
密文后端解密的javax.crypto.BadPaddingException: pad block corrupted(后端语言为java)错误;
密文前端解密的
Illegal Buffer at WXBizDataCrypt.define.push.WXBizDataCrypt.decryptData
错误。

二、错误原因

在回调里面又调用一次wx.login()导致登录状态被刷新,此时用code换取的sessionKey,不是的当时encryptedData对应的sessionKey,这个时候就导致了解密失败。
例如:
在这里插入图片描述

由于**@getphonenumber是获取用户手机号回调方法,然后又在回调中调用了一次login()**,导致出现上述问题。

三、解决方法

3.1 不推荐方法

当出现上述问题的时候,可以弹出提示,引导用户再点击一次授权,出现两次错误的几率还是不高的,业务允许的话,可以这么干。

3.2 正确方法

试想一下,出现问题的原因是啥,当然是在回调里面又重新login()了一下,那么能不能把login()放在回调外面。
也就是先调用
login()方法获取code(不要在@getphonenumbe里面调用login)
,然后在获取sessionKey,解密数据。

四、附录

4.1 后端解密方法

import com.alibaba.fastjson.JSONObject;

WXBizDataCryptUtil util = new WXBizDataCryptUtil(properties.getAppId(), dto.getSessionKey());
JSONObject data = util.decrypt(dto.getEncryptedData(), dto.getIv());
import com.alibaba.fastjson.JSONObject;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
import java.util.Base64;

/**
 * 微信数据解密工具
 *
 * @date 2023-06-08 16:51:26
 */
public class WXBizDataCryptUtil {

    public static JSONObject getMoreInfoFromEncryptedData(String appId, String sessionKey, String encryptedData, String iv) {
        WXBizDataCryptUtil pc = new WXBizDataCryptUtil(appId, sessionKey);
        JSONObject decrypt = pc.decrypt(encryptedData, iv);
        return decrypt;
    }

    private String appId;
    private String sessionKey;

    public WXBizDataCryptUtil(String appId, String sessionKey) {
        this.appId = appId;
        this.sessionKey = sessionKey;
    }

    /**
     * 解密成json
     *
     * @param encryptedData
     * @param iv
     * @return
     */
    public JSONObject decrypt(String encryptedData, String iv) {
        byte[] encryptedDataDecode = Base64.getDecoder().decode(encryptedData);
        byte[] sessionKeyDecode = Base64.getDecoder().decode(this.sessionKey);
        byte[] ivDecode = Base64.getDecoder().decode(iv);
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        byte[] decodeData = decode(encryptedDataDecode, sessionKeyDecode, ivDecode);
        String stringData = new String(decodeData);
        JSONObject jsonObject = JSONObject.parseObject(stringData);
        return jsonObject;
    }
    /**
     * 解密算法 AES-128-CBC
     * 填充模式 PKCS#7
     *
     * @param encryptedDataDecode 目标密文
     * @return
     * @throws Exception
     */
    private byte[] decode(byte[] encryptedDataDecode, byte[] sessionKeyDecode, byte[] iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            Key sKeySpec = new SecretKeySpec(sessionKeyDecode, "AES");
            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIv(iv));// 初始化
            byte[] result = cipher.doFinal(encryptedDataDecode);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 处理iv
     *
     * @param iv
     * @return
     * @throws Exception
     */
    private AlgorithmParameters generateIv(byte[] iv) throws Exception {
        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
        params.init(new IvParameterSpec(iv));
        return params;
    }

}

4.2 前端解密

getPhoneNumber(e) {
  if (e.detail.errMsg === 'getPhoneNumber:ok') {
    getOpenId({
      code: this.wxCode
    })
      .then(openIdRes => {
        if (!openIdRes.success) {
          this.$refs.page.closeLoading()
          showErrorToast(this.$refs.uToast, {
            position: 'top',
            message: openIdRes.message
          })
          return
        }
        const data = openIdRes.data
        const wxCryto = new WXBizDataCrypt(data.appId, data.sessionKey)
        const encryptData = wxCryto.decryptData(e.detail.encryptedData, e.detail.iv)
        console.log('解密结果', encryptData)
      })
      .catch(err => {
        console.log(err)
        this.$refs.page.closeLoading()
        showErrorToast(this.$refs.uToast, {
          position: 'top',
          message: '获取微信参数异常, 请重试'
        })
      })
  }
}
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
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱旅行的小杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值