為了確保 開放接口 返回用戶數(shù)據(jù)的安全性,微信會(huì)對(duì)明文數(shù)據(jù)進(jìn)行簽名。開發(fā)者可以根據(jù)業(yè)務(wù)需要對(duì)數(shù)據(jù)包進(jìn)行簽名校驗(yàn),確保數(shù)據(jù)的完整性。
接口如果涉及敏感數(shù)據(jù)(如wx.getUserInfo當(dāng)中的 openId 和unionId ),接口的明文內(nèi)容將不包含這些敏感數(shù)據(jù)。開發(fā)者如需要獲取敏感數(shù)據(jù),需要對(duì)接口返回的加密數(shù)據(jù)( encryptedData )進(jìn)行對(duì)稱解密。解密算法如下:
微信官方提供了多種編程語言的示例代碼( 點(diǎn)擊下載 ),但就是沒提供JAVA版本的,可能的確PHP是最好的語言,騰訊提供的demo好多都是PHP版本的。
pom.xml引入以下依賴:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.57</version>
</dependency>
我們可以參考PHP給出的代碼,使用JAVA實(shí)現(xiàn): AESUtil:
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;
/**
* AES解密
* 創(chuàng)建者 柒
* 創(chuàng)建時(shí)間 2018年3月12日
*/
public class AESUtil {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* AES解密
* @param content 密文
* @return
* @throws InvalidAlgorithmParameterException
* @throws NoSuchProviderException
*/
public static byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte)
throws InvalidAlgorithmParameterException {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key sKeySpec = new SecretKeySpec(keyByte, "AES");
//生成iv
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, params);// 初始化
return cipher.doFinal(content);
} 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 (Exception e) {
e.printStackTrace();
}
return null;
}
}
WXBizDataCrypt:
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
* 對(duì)微信小程序用戶加密數(shù)據(jù)的解密
* 創(chuàng)建者 柒
* 創(chuàng)建時(shí)間 2018年3月12日
*/
public class WXBizDataCrypt {
public static String illegalAesKey = "-41001";//非法密鑰
public static String illegalIv = "-41002";//非法初始向量
public static String illegalBuffer = "-41003";//非法密文
public static String decodeBase64Error = "-41004"; //解碼錯(cuò)誤
public static String noData = "-41005"; //數(shù)據(jù)不正確
private String appid;
private String sessionKey;
public WXBizDataCrypt(String appid, String sessionKey) {
this.appid = appid;
this.sessionKey = sessionKey;
}
/**
* 檢驗(yàn)數(shù)據(jù)的真實(shí)性,并且獲取解密后的明文.
* @param encryptedData string 加密的用戶數(shù)據(jù)
* @param iv string 與用戶數(shù)據(jù)一同返回的初始向量
* @return data string 解密后的原文
* @return String 返回用戶信息
*/
public String decryptData(String encryptedData, String iv) {
if (StringUtils.length(sessionKey) != 24) {
return illegalAesKey;
}
// 對(duì)稱解密秘鑰 aeskey = Base64_Decode(session_key), aeskey 是16字節(jié)。
byte[] aesKey = Base64.decodeBase64(sessionKey);
if (StringUtils.length(iv) != 24) {
return illegalIv;
}
// 對(duì)稱解密算法初始向量 為Base64_Decode(iv),其中iv由數(shù)據(jù)接口返回。
byte[] aesIV = Base64.decodeBase64(iv);
// 對(duì)稱解密的目標(biāo)密文為 Base64_Decode(encryptedData)
byte[] aesCipher = Base64.decodeBase64(encryptedData);
try {
byte[] resultByte = AESUtil.decrypt(aesCipher, aesKey, aesIV);
if (null != resultByte && resultByte.length > 0) {
String userInfo = new String(resultByte, "UTF-8");
JSONObject jsons = JSON.parseObject(userInfo);
String id = jsons.getJSONObject("watermark").getString("appid");
if (!StringUtils.equals(id, appid)) {
return illegalBuffer;
}
return userInfo;
} else {
return noData;
}
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* encryptedData 和 iv 兩個(gè)參數(shù)通過小程序wx.getUserInfo()方法獲取
* @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));
}
}
運(yùn)行main方法,獲取返回結(jié)果:
{"openId":"oGZUI0egBJY1zhBYw2KhdUfwVJJE","nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0","unionId":"ocMvos6NjeKLIBqg5Mr9QjxrP1FA","watermark":{"timestamp":1477314187,"appid":"wx4f4bc4dec97d474b"}}