• 微信退款通知信息解密


     在最近的开发需求中,有一个需求,就是需要把微信退款通知记录在数据库中,原本以为是一个简单的需求,但是微信文档的坑,是你不能理解的。

     在微信退款通知返回的字段中有一个加密信息字段req_info。这个加密字段需要三个解密步骤才能最终获取到信息。由此可见,这个字段是多么的重要。以下是微信官方文档给出的解密步骤:

    解密步骤如下:

    (1)对加密串A做base64解码,得到加密串B

    (2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )

    (3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)

    第一步,第二步都好理解,但是第三步说的真是太简单了。被坑了一天,现在把代码留下。

     1.通过微信退款通知先获取到req_info这个字段

    public static Map<String, String> xmlToMap(HttpServletRequest request) throws Exception {
    Map<String, String> map = new HashMap<String, String>();
    SAXReader reader = new SAXReader();
    InputStream ins = request.getInputStream();
    Document doc = reader.read(ins);
    Element root = doc.getRootElement();
    List<Element> list = root.elements();
    for (Element e : list) {
    map.put(e.getName(), e.getText());
    }
    ins.close();
    return map;
    }

    这里使用DOM4j 首先把返回来的xml转换成map。从中获取req_info。

    2.就是对req_info这个字段信息进行解密。

        1. 对加密信息进行base64位解码。

          byte[] b = org.bouncycastle.util.encoders.Base64.decode(req_info);

        2.对商户API秘钥做MD5加密,得到小写的32位key

         String key* = getMD5("your  API key").toLowerCase().getBytes();

       3.用刚才得到的key对加密串B做AES-256-ECB解密(PKCS7Padding) 

    public static String decryptData(byte[] b) throws Exception {
    Cipher cipher = null;
    cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    SecretKeySpec key = new SecretKeySpec(key*.getBytes(), "AES");
    cipher.init(Cipher.DECRYPT_MODE, key);
    return new String(cipher.doFinal(b));
    }

    解密过后,就会返还xml,需要对xml进一步解析。

    在整个解密过程中需要注意以下几点:

     1. java 目前只支持PKCS5Padding,所以需要额外添加依赖。

    <dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-ext-jdk16</artifactId>
    <version>1.45</version>
    </dependency>

    2.如果报这个错 Illegal key size or default parameters 就需要替换你JAVA_HOME/jre/lib/security 下面的两个jar包 local_policy.jar和US_export_policy.jar。

    3.在解密类中需要添加一个静态代码块来提供支持,静态代码块如下:

    static {
    try {
    Security.addProvider(new BouncyCastleProvider());
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    以下 附上完整代码:

     public class CipherTextUtil  {

        private static final String serectKey = "your API Serect key";

    /**
    * 密钥算法
    */
    private static final String ALGORITHM = "AES";
    /**
    * 加解密算法/工作模式/填充方式
    */
    private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
    /**
    * 生成key
    */
    private static SecretKeySpec key = new SecretKeySpec(MD5Util.MD5Encode(serectKey).toLowerCase().getBytes(), ALGORITHM);

    static {
    try {
    Security.addProvider(new BouncyCastleProvider());
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * AES解密
    */
    public static String decryptData(byte[] b) throws Exception {
    Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
    cipher.init(Cipher.DECRYPT_MODE, key);
    return new String(cipher.doFinal(b));
    }


    public static void main(String[] args) throws Exception {

    String A = "cms0dmeMS39cUWRUz19yJiPifeLRXoZjXhOnIhQlrz4SxDa2kmPVxHkMv2BeE+H+xr8euGyAO9sBNmS9ffugL7Xj47b9K6bwI8BLrP6/SwL3nTcrm44oimbJ4axAQLoaNuheqWYFS7pRBXAbByjjsBLzdMh+thCJjhFvyCK61fRMaUlkn6eZsc7fs609KgELGqSBdDlNsbXpTurgqdLdW07jPlsJZyXshg1gJ3b+n2wMC/pEZwPA/w7E0bAt0xzTIsYKmtLEwJlWg6Vf1KiocDfU0oVnhd5JPozxx4Cve67kUuGqDpdoStTBMXywHoAytklE610DvVKT59gkK9MqFU97SXV714wko6ZRKQAW0Utpx9DUko4jV3Hdwspr1JYUAg/GnINom25YbWQqKp28VKkJveXGeif5GEeUxeAmr4mYGvf9vD+bvBP2sH06koiOTJVVuIhvWmAdmFnNdk2hJkJdKvEtxxaNP7/eha5jDKNgjvCGV/hJNasl8U3qZX1qR0RtvcgoORt3nNvUOjQESNeS+tXlZtcpdZqDR4g7kQl5grAs+BOF6rk0qJ0PagxWF+arDSReLYBN5ihBkvai56s5Cv9iXFpVNkMwTBqYC4OpqzIN1EecA5fpi9pk7nVB3v5HLwp3oBYhbNiPNXkGTkuw634wBQtDLmeG3mXAYTX4xWdsYrkg4RY/rqFKsgeeBzGs7a7+PgbcE8B/t4/jVX3qVwbBkacqhG1l/W0zfZA8vHOJzVJhWEjXzn1iccQLiA6OASfnvRYoPnBoCX1qIjo0e55oCvRDau7hVTSvBx0hcpIjXmlcMcpJgiRXrPZFXgWh9myRkYtkCRleX49NELBOnK+sRHuJlfnpYYs9d1TurWzhXuu6wMow/wGQtyGC3XtQv0QTTg1KKDPRiqYq0QXMJ28+jL216Eg8qGrxmzXGYJ9Nf3oLh2TOZZvdhBbj+EM8T9+c/h3bAEe2L1wYtEJrqAMiSwZ6qW4dElQHBwL7sWha63gIB37vJ0SbyjWkqWWLUwwOMkDgvJ9r5I58O4OgzBtinC9k9yl90wO/dm0yuDTwF9agYlsPFP8XXClVdnvGQd+HUPk9YKB+WkyUww==";
    byte[] b = Base64.decode(A);
    String B = AESUtil.decryptData(b);
    System.out.println(B);


    }
    }

    完结,撒花撒花。。。。。

  • 相关阅读:
    js判断时间间隔
    redis 常用命令
    Spring 启动 自动调用方法的两种形式
    多线程的异常处理
    多线程Monitor.TryEnter(有一个聪明的员工找老板。看到老板们在里面都掐成一团乱麻了,算了我还是撩吧)
    多线程中多个join的执行过程
    多线程之向线程传递参数
    ASP.Net Core下的安全(授权、身份验证、ASP.NET Core Identity)
    C# 中常用的索引器(转)
    《戏班的故事》C#基础之多线程之“前台线程-后台线程”
  • 原文地址:https://www.cnblogs.com/yelele/p/8473895.html
Copyright © 2020-2023  润新知