From 75c10cb035564134968c8a7c7662c40ab33e0a02 Mon Sep 17 00:00:00 2001 From: Sutra Zhou Date: Mon, 30 Apr 2018 15:56:39 +0800 Subject: [PATCH] Add WXBizDataCrypt for WeChat Mini Program. --- .gitignore | 8 +-- README.md | 6 +- pom.xml | 1 + weixin4j-wxa/pom.xml | 49 ++++++++++++++++ .../com/foxinmy/weixin4j/wxa/AESUtils.java | 58 +++++++++++++++++++ .../foxinmy/weixin4j/wxa/WXBizDataCrypt.java | 48 +++++++++++++++ .../foxinmy/weixin4j/wxa/package-info.java | 4 ++ .../weixin4j/wxa/WXBizDataCryptTest.java | 55 ++++++++++++++++++ 8 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 weixin4j-wxa/pom.xml create mode 100644 weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/AESUtils.java create mode 100644 weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java create mode 100644 weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/package-info.java create mode 100644 weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/WXBizDataCryptTest.java diff --git a/.gitignore b/.gitignore index 1d3be1be..dd217e55 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,9 @@ hs_err_pid* *~ # eclipse ignore -*.settings/* -/.project -/.classpath +.settings/ +.project +.classpath /.tomcatplugin # idea ignore @@ -22,7 +22,7 @@ hs_err_pid* *.iml # maven ignore -target/* +target/ # other ignore *.log diff --git a/README.md b/README.md index a9347797..d47a3810 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,11 @@ weixin4j * **weixin4j-qy[1.7.8]** `企业号API封装` - + +* **weixin4j-wxa[1.7.9]** + + `小程序 API 封装` + * **weixin4j-server[1.1.8]** `netty服务器&消息分发` diff --git a/pom.xml b/pom.xml index a586f9e7..11cfda02 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,7 @@ weixin4j-base weixin4j-mp weixin4j-qy + weixin4j-wxa weixin4j-server weixin4j-example weixin4j-serverX diff --git a/weixin4j-wxa/pom.xml b/weixin4j-wxa/pom.xml new file mode 100644 index 00000000..1a552d0d --- /dev/null +++ b/weixin4j-wxa/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + com.foxinmy + weixin4j + 1.7.9 + + weixin4j-wxa + weixin4j-wxa + https://github.com/foxinmy/weixin4j/tree/master/weixin4j-wxa + 微信小程序 API 支持 + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.foxinmy + weixin4j-base + ${project.version} + + + commons-codec + commons-codec + 1.10 + + + org.bouncycastle + bcprov-jdk15on + 1.55 + + + junit + junit + + + ch.qos.logback + logback-core + 1.1.8 + test + + + \ No newline at end of file diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/AESUtils.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/AESUtils.java new file mode 100644 index 00000000..4b445b66 --- /dev/null +++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/AESUtils.java @@ -0,0 +1,58 @@ +package com.foxinmy.weixin4j.wxa; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.Key; +import java.security.NoSuchProviderException; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +final class AESUtils { + + private static boolean initialized = false; + + private AESUtils() { + } + + /** + * AES解密 + * + * @param content 密文 + * @return 明文 + * @throws InvalidAlgorithmParameterException + * @throws NoSuchProviderException + */ + 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 (Exception e) { + throw new RuntimeException(e); + } + } + + private static void initialize() { + if (initialized) + return; + Security.addProvider(new BouncyCastleProvider()); + initialized = true; + } + + private static AlgorithmParameters generateIV(byte[] iv) throws Exception { + AlgorithmParameters params = AlgorithmParameters.getInstance("AES"); + params.init(new IvParameterSpec(iv)); + return params; + } + +} diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java new file mode 100644 index 00000000..d65e4329 --- /dev/null +++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/WXBizDataCrypt.java @@ -0,0 +1,48 @@ +package com.foxinmy.weixin4j.wxa; + +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; + +import org.apache.commons.codec.binary.Base64; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +/** + * @see 加密数据解密算法 + */ +public class WXBizDataCrypt { + + private final String appid; + + private final String sessionKey; + + public WXBizDataCrypt(String appid, String sessionKey) { + this.appid = appid; + this.sessionKey = sessionKey; + } + + public JSONObject decryptData(final String encryptedData, final String iv) { + final byte[] aesKey = Base64.decodeBase64(sessionKey); + final byte[] aesCipher = Base64.decodeBase64(encryptedData); + final byte[] aesIV = Base64.decodeBase64(iv); + + final byte[] resultByte; + try { + resultByte = AESUtils.decrypt(aesCipher, aesKey, aesIV); + } catch (InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + + final String decryptedText = new String(resultByte, Charset.forName("UTF-8")); + final JSONObject decrypted = JSON.parseObject(decryptedText); + + final String appId = decrypted.getJSONObject("watermark").getString("appid"); + if (!appId.equals(this.appid)) { + throw new IllegalArgumentException("Invalid Buffer"); + } + + return decrypted; + } + +} diff --git a/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/package-info.java b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/package-info.java new file mode 100644 index 00000000..6a4bfcc5 --- /dev/null +++ b/weixin4j-wxa/src/main/java/com/foxinmy/weixin4j/wxa/package-info.java @@ -0,0 +1,4 @@ +/** + * WeChat Mini Program support library. + */ +package com.foxinmy.weixin4j.wxa; diff --git a/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/WXBizDataCryptTest.java b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/WXBizDataCryptTest.java new file mode 100644 index 00000000..8743e9dc --- /dev/null +++ b/weixin4j-wxa/src/test/java/com/foxinmy/weixin4j/wxa/WXBizDataCryptTest.java @@ -0,0 +1,55 @@ +package com.foxinmy.weixin4j.wxa; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.alibaba.fastjson.JSONObject; + +public class WXBizDataCryptTest { + + @Test + public void testDecryptData() { + String appId = "wx4f4bc4dec97d474b"; + String sessionKey = "tiihtNczf5v6AKRyjwEUhQ=="; + + WXBizDataCrypt biz = new WXBizDataCrypt(appId, sessionKey); + + 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=="; + + JSONObject data = biz.decryptData(encryptedData, iv); + + assertEquals("CN", data.getString("country")); + assertEquals("ocMvos6NjeKLIBqg5Mr9QjxrP1FA", data.getString("unionId")); + assertEquals(1, data.getIntValue("gender")); + assertEquals("Guangdong", data.getString("province")); + assertEquals("Guangzhou", data.getString("city")); + assertEquals("http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0", data.getString("avatarUrl")); + assertEquals("oGZUI0egBJY1zhBYw2KhdUfwVJJE", data.getString("openId")); + assertEquals("Band", data.getString("nickName")); + assertEquals("zh_CN", data.getString("language")); + + JSONObject watermark = data.getJSONObject("watermark"); + assertEquals("wx4f4bc4dec97d474b", watermark.getString("appid")); + assertEquals(1477314187L, watermark.getLongValue("timestamp")); + } + +}