Java之—实现微信小程序加密数据解密算法

发布时间:2018-10-12 09:42:10   来源:网络
原文发布时间:2018-08-16 08:43
原文作者:程序猿的内心独白

一、概述

微信推出了小程序,很多公司的客户端应用不仅具有了APP、H5、还接入了小程序开发。但是,小程序中竟然没有提供Java版本的加密数据解密算法。这着实让广大的Java开发人员蛋疼。

微信小程序提供的加密数据解密算法链接为:https://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html

我们下载的算法示例如下:

木有Java!! 木有Java!! 木有Java!!

那么如何解决这个问题,我们一起来实现Java版本的微信小程序加密数据解密算法。

二、实现Java版本的微信小程序加密数据解密算法

1、创建项目

这里,我们创建一个Maven工程,具体创建步骤略。

2、配置pom.xml

我们在pom.xml中加入如下配置。


  1. <dependency>
  2. <groupId>org.bouncycastle</groupId>
  3. <artifactId>bcprov-jdk16</artifactId>
  4. <version>1.46</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-codec</groupId>
  8. <artifactId>commons-codec</artifactId>
  9. <version>1.4</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>net.sf.json-lib</groupId>
  13. <artifactId>json-lib</artifactId>
  14. <version>2.2.3</version>
  15. <classifier>jdk15</classifier>
  16. </dependency>

3、实现AES类


  1. package com.chwl.medical.crypto.wx;
  2. import java.security.AlgorithmParameters;
  3. import java.security.InvalidAlgorithmParameterException;
  4. import java.security.InvalidKeyException;
  5. import java.security.Key;
  6. import java.security.NoSuchAlgorithmException;
  7. import java.security.NoSuchProviderException;
  8. import java.security.Security;
  9. import javax.crypto.BadPaddingException;
  10. import javax.crypto.Cipher;
  11. import javax.crypto.IllegalBlockSizeException;
  12. import javax.crypto.NoSuchPaddingException;
  13. import javax.crypto.spec.IvParameterSpec;
  14. import javax.crypto.spec.SecretKeySpec;
  15. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  16. /**
  17. * AES加密
  18. * @author liuyazhuang
  19. *
  20. */
  21. public class AES {
  22. public static boolean initialized = false;
  23. /**
  24. * AES解密
  25. *
  26. * @param content
  27. * 密文
  28. * @return
  29. * @throws InvalidAlgorithmParameterException
  30. * @throws NoSuchProviderException
  31. */
  32. public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
  33. initialize();
  34. try {
  35. Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
  36. Key sKeySpec = new SecretKeySpec(keyByte, "AES");
  37. cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
  38. byte[] result = cipher.doFinal(content);
  39. return result;
  40. } catch (NoSuchAlgorithmException e) {
  41. e.printStackTrace();
  42. } catch (NoSuchPaddingException e) {
  43. e.printStackTrace();
  44. } catch (InvalidKeyException e) {
  45. e.printStackTrace();
  46. } catch (IllegalBlockSizeException e) {
  47. e.printStackTrace();
  48. } catch (BadPaddingException e) {
  49. e.printStackTrace();
  50. } catch (NoSuchProviderException e) {
  51. // TODO Auto-generated catch block
  52. e.printStackTrace();
  53. } catch (Exception e) {
  54. // TODO Auto-generated catch block
  55. e.printStackTrace();
  56. }
  57. return null;
  58. }
  59. public static void initialize() {
  60. if (initialized)
  61. return;
  62. Security.addProvider(new BouncyCastleProvider());
  63. initialized = true;
  64. }
  65. // 生成iv
  66. public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
  67. AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
  68. params.init(new IvParameterSpec(iv));
  69. return params;
  70. }
  71. }

4、实现WxPKCS7Encoder类


  1. package com.chwl.medical.crypto.wx;
  2. import java.nio.charset.Charset;
  3. import java.util.Arrays;
  4. /**
  5. * 微信小程序加解密
  6. * @author liuyazhuang
  7. *
  8. */
  9. public class WxPKCS7Encoder {
  10. private static final Charset CHARSET = Charset.forName("utf-8");
  11. private static final int BLOCK_SIZE = 32;
  12. /**
  13. * 获得对明文进行补位填充的字节.
  14. *
  15. * @param count
  16. * 需要进行填充补位操作的明文字节个数
  17. * @return 补齐用的字节数组
  18. */
  19. public static byte[] encode(int count) {
  20. // 计算需要填充的位数
  21. int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
  22. if (amountToPad == 0) {
  23. amountToPad = BLOCK_SIZE;
  24. }
  25. // 获得补位所用的字符
  26. char padChr = chr(amountToPad);
  27. String tmp = new String();
  28. for (int index = 0; index < amountToPad; index++) {
  29. tmp += padChr;
  30. }
  31. return tmp.getBytes(CHARSET);
  32. }
  33. /**
  34. * 删除解密后明文的补位字符
  35. *
  36. * @param decrypted
  37. * 解密后的明文
  38. * @return 删除补位字符后的明文
  39. */
  40. public static byte[] decode(byte[] decrypted) {
  41. int pad = decrypted[decrypted.length - 1];
  42. if (pad < 1 || pad > 32) {
  43. pad = 0;
  44. }
  45. return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
  46. }
  47. /**
  48. * 将数字转化成ASCII码对应的字符,用于对明文进行补码
  49. *
  50. * @param a
  51. * 需要转化的数字
  52. * @return 转化得到的字符
  53. */
  54. public static char chr(int a) {
  55. byte target = (byte) (a & 0xFF);
  56. return (char) target;
  57. }
  58. }

5、实现WXCore类

这个类主要是对具体算法的封装,统一对外提供方法。


  1. package com.chwl.medical.crypto.wx;
  2. import org.apache.commons.codec.binary.Base64;
  3. import net.sf.json.JSONObject;
  4. /**
  5. * 封装对外访问方法
  6. * @author liuyazhuang
  7. *
  8. */
  9. public class WXCore {
  10. private static final String WATERMARK = "watermark";
  11. private static final String APPID = "appid";
  12. /**
  13. * 解密数据
  14. * @return
  15. * @throws Exception
  16. */
  17. public static String decrypt(String appId, String encryptedData, String sessionKey, String iv){
  18. String result = "";
  19. try {
  20. AES aes = new AES();
  21. byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
  22. if(null != resultByte && resultByte.length > 0){
  23. result = new String(WxPKCS7Encoder.decode(resultByte));
  24. JSONObject jsonObject = JSONObject.fromObject(result);
  25. String decryptAppid = jsonObject.getJSONObject(WATERMARK).getString(APPID);
  26. if(!appId.equals(decryptAppid)){
  27. result = "";
  28. }
  29. }
  30. } catch (Exception e) {
  31. result = "";
  32. e.printStackTrace();
  33. }
  34. return result;
  35. }
  36. public static void main(String[] args) throws Exception{
  37. String appId = "wx4f4bc4dec97d474b";
  38. String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==";
  39. String sessionKey = "tiihtNczf5v6AKRyjwEUhQ==";
  40. String iv = "r7BXXKkLb8qrSNn05n0qiA==";
  41. System.out.println(decrypt(appId, encryptedData, sessionKey, iv));
  42. }
  43. }

三、测试

1、运行Java版微信小程序加密数据解密算法

这里我们就直接运行WXcore类的main方法,这里的测试数据都是从Python版微信小程序加密数据解密算法的示例程序中提出来的。我们的运行结果如下:

{"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"}}

2、运行Python版微信小程序加密数据解密算法

这里我们在python环境中直接运行微信官方提供的Python版小程序加密数据解密算法,结果如下:

{u'province': u'Guangdong', u'openId': u'oGZUI0egBJY1zhBYw2KhdUfwVJJE', u'language': u'zh_CN', u'city': u'Guangzhou', u'gender': 1, u'avatarUrl': u'http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0', u'watermark': {u'timestamp': 1477314187, u'appid': u'wx4f4bc4dec97d474b'}, u'country': u'CN', u'nickName': u'Band', u'unionId': u'ocMvos6NjeKLIBqg5Mr9QjxrP1FA'}

通过对比以上结果可知,我们自行使用Java实现的Java版微信小程序加密数据解密算法与微信官方提供的Python版小程序加密数据解密算法结果一致。

推荐JAVA入门学习书籍:

转载:http://blog.csdn.net/l1028386804/article/details/79450115


延伸阅读: