好久没写博客了,今天部门小兄弟来问微信小程序 怎么对微信服务器传过来的用户数据解密,进而获得openid和昵称头像等。网上的资料不多,不得不说腾讯太喜欢PHP了,demo好多都是PHP版本的。
这里是腾讯相关API:https://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html
自己翻译了下PHP版本的,也顺便发到博客上,以让后来的同学活得容易点。
用到maven依赖:
commons-codec
commons-codec
1.9
com.alibaba
fastjson
1.1.32
org.bouncycastle
bcprov-jdk15on
1.55
AES工具类:
/*
* 文件名:AESUtils.java
* 版权:
* 描述:
* 修改人:Awoke
* 修改时间:2018-1-24
* 跟踪单号:
* 修改单号:
* 修改内容:
*/
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
*
* @author Awoke
* @version 2018-1-24
* @see AESUtils
* @since
*/
public class AESUtils
{
public static boolean initialized = false;
/**
* AES解密
* @param content 密文
* @return
* @throws InvalidAlgorithmParameterException
* @throws NoSuchProviderException
*/
public static byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte)
throws InvalidAlgorithmParameterException
{
initialize();
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key sKeySpec = new SecretKeySpec(keyByte, "AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
byte[] result = cipher.doFinal(content);
return result;
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (NoSuchProviderException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static void initialize()
{
if (initialized) return;
Security.addProvider(new BouncyCastleProvider());
initialized = true;
}
/**
* 生成iv
* @param iv
* @return
* @throws Exception
* @see
*/
public static AlgorithmParameters generateIV(byte[] iv)
throws Exception
{
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(iv));
return params;
}
}
WXBizDataCrypt.java
/*
* 文件名:WXBizDataCrypt.java
* 版权:
* 描述:
* 修改人:Awoke
* 修改时间:2018-1-24
* 跟踪单号:
* 修改单号:
* 修改内容:
*/
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sinocjh.bp.security.AESUtils;
/**
* 对微信小程序用户加密数据的解密示例代码.
* @author Awoke
* @version 2018-1-24
* @see WXBizDataCrypt
* @since
*/
public class WXBizDataCrypt
{
private String appid;
private String sessionKey;
public WXBizDataCrypt(String appid, String sessionKey)
{
this.appid = appid;
this.sessionKey = sessionKey;
}
/**
* 检验数据的真实性,并且获取解密后的明文.
* @param encryptedData string 加密的用户数据
* @param iv string 与用户数据一同返回的初始向量
*
* @return data string 解密后的原文
*/
public String decryptData(String encryptedData, String iv)
{
if (StringUtils.length(sessionKey) != 24)
{
return "ErrorCode::$IllegalAesKey;";
}
// 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
byte[] aesKey = Base64.decodeBase64(sessionKey);
if (StringUtils.length(iv) != 24)
{
return "ErrorCode::$IllegalIv;";
}
// 对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
byte[] aesIV = Base64.decodeBase64(iv);
// 对称解密的目标密文为 Base64_Decode(encryptedData)
byte[] aesCipher = Base64.decodeBase64(encryptedData);
Map map = new HashMap<>();
try
{
byte[] resultByte = AESUtils.decrypt(aesCipher, aesKey, aesIV);
if (null != resultByte && resultByte.length > 0)
{
String userInfo = new String(resultByte, "UTF-8");
map.put("code", "0000");
map.put("msg", "succeed");
map.put("userInfo", userInfo);
// watermark参数说明:
// 参数 类型 说明
// watermark OBJECT 数据水印
// appid String 敏感数据归属appid,开发者可校验此参数与自身appid是否一致
// timestamp DateInt 敏感数据获取的时间戳, 开发者可以用于数据时效性校验'
// 根据微信建议:敏感数据归属appid,开发者可校验此参数与自身appid是否一致
// if decrypted['watermark']['appid'] != self.appId:
JSONObject jsons = JSON.parseObject(userInfo);
String id = jsons.getJSONObject("watermark").getString("appid");
if(!StringUtils.equals(id, appid))
{
return "ErrorCode::$IllegalBuffer;";
}
}
else
{
map.put("status", "1000");
map.put("msg", "false");
}
}
catch (InvalidAlgorithmParameterException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return JSON.toJSONString(map);
}
/**
*
* @param args
* @see
*/
public static void main(String[] args)
{
String appId = "wx4f4bc4dec97d474b";
String sessionKey = "tiihtNczf5v6AKRyjwEUhQ==";
String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZM"
+ "QmRzooG2xrDcvSnxIMXFufNstNGTyaGS"
+ "9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+"
+ "3hVbJSRgv+4lGOETKUQz6OYStslQ142d"
+ "NCuabNPGBzlooOmB231qMM85d2/fV6Ch"
+ "evvXvQP8Hkue1poOFtnEtpyxVLW1zAo6"
+ "/1Xx1COxFvrc2d7UL/lmHInNlxuacJXw"
+ "u0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn"
+ "/Hz7saL8xz+W//FRAUid1OksQaQx4CMs"
+ "8LOddcQhULW4ucetDf96JcR3g0gfRK4P"
+ "C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB"
+ "6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns"
+ "/8wR2SiRS7MNACwTyrGvt9ts8p12PKFd"
+ "lqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV"
+ "oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG"
+ "20f0a04COwfneQAGGwd5oa+T8yO5hzuy" + "Db/XcxxmK01EpqOyuxINew==";
String iv = "r7BXXKkLb8qrSNn05n0qiA==";
WXBizDataCrypt biz = new WXBizDataCrypt(appId, sessionKey);
System.out.println(biz.decryptData(encryptedData, iv));
}
}
代码细节需要优化下,不是最优,请自处理。