package cn.common.common.utils.wx.wxpay;

import cn.common.common.interaction.WxPhoneNumberWrap;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.InvalidParameterSpecException;
import java.util.Base64;

/**
 * @author Singer create by Singer email:singer-coder@qq.com
 * @packageName cn.common.common.utils
 * @Description: 微信工具
 * @date 2021-01-26
 */
@Slf4j
public class AesForWxPhoneNumberUtils {

    /**
     * 加密方式
     */
    private static String keyAlgorithm = "AES";

    /**
     * 避免重复new生成多个BouncyCastleProvider对象，因为GC回收不了，会造成内存溢出
     * 只在第一次调用decrypt()方法时才new 对象
     */
    private static boolean initialized = false;

    /**
     * 用于Base64解密
     */
    private Base64.Decoder decoder = Base64.getDecoder();

    /**
     * 待解密的数据
     */
    private String originalContent;

    /**
     * 会话密钥sessionKey
     */
    private String encryptKey;

    /**
     * 加密算法的初始向量
     */
    private String iv;

    public AesForWxPhoneNumberUtils(String originalContent, String encryptKey, String iv) {
        this.originalContent = originalContent;
        this.encryptKey = encryptKey;
        this.iv = iv;
    }

    /**
     * AES解密
     * 填充模式AES/CBC/PKCS7Padding
     * 解密模式128
     *
     * @return 解密后的信息对象
     */
    public WxPhoneNumberWrap decrypt() throws Exception{
        initialize();
        try {
            //数据填充方式
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            Key sKeySpec = new SecretKeySpec(decoder.decode(this.encryptKey), keyAlgorithm);
            // 初始化
            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIv(decoder.decode(this.iv)));
            byte[]data = cipher.doFinal(decoder.decode(this.originalContent));
            String datastr = new String(data, StandardCharsets.UTF_8);
            return JSON.toJavaObject(JSON.parseObject(datastr),WxPhoneNumberWrap.class);
        } catch (Exception e) {
            log.error("解密微信数据出现异常 ：{},{} ",e.getMessage(),e);
            throw new Exception("解密微信数据出现异常");
        }
    }

    /**BouncyCastle作为安全提供，防止我们加密解密时候因为jdk内置的不支持改模式运行报错。**/
    private static void initialize() {
        if (initialized) {
            return;
        }
        Security.addProvider(new BouncyCastleProvider());
        initialized = true;
    }

    /**
     * 生成iv
     * @author: create by singer - Singer email:singer-coder@qq.com
     * @date 2021/3/29
     * @param iv 偏倚量
     * @return AlgorithmParameters
     */
    private static AlgorithmParameters generateIv(byte[] iv) throws NoSuchAlgorithmException,
            InvalidParameterSpecException {
        AlgorithmParameters params = AlgorithmParameters.getInstance(keyAlgorithm);
        params.init(new IvParameterSpec(iv));
        return params;
    }

    public static void main(String[] args) {
        //加密的敏感数据
        String encryptedData = "FOyENjYiBj161nsxUGyzzvtnNnRr8+bTuPcIkioY7Rq+j3by/xXAmH2Scwe/YjKt76+Z6hFQBpZnSiVcSr8L1wQWQcRahkEK5S+Lzh6GGjIGIJnbpcSPQYnoIEhGH1hqg3+6ELocI8hSExoJoKDy6nSHELJVRohUU4skE1aaZNSSYjp/DQM3buCgPZNvDDyQjBCOUkjpQKo1xuSBYLcYSg==";
        //初始向量
        String iv = "NQgc0+2yU8JII/W/gr+vgw==";
        //会话密钥
        String sessionKey = "Zr1ng5U3n0+ftGuP2s+xHA==";
        //appId
        String appId = "wx3a29d2404d180256";
        AesForWxPhoneNumberUtils aes = new AesForWxPhoneNumberUtils(encryptedData,sessionKey,iv);
        WxPhoneNumberWrap result = null;
        try {
            result = aes.decrypt();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (null==result){
            System.out.println("error");
        }else {
            System.out.println(result.toString());
            if (!result.getWatermark().getAppId().equals(appId)){
                System.out.println("wrong appId");
            }
        }
    }
}
