package cn.com.poc.common.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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;

/**
 * @author Focan.Zhong
 */
public class WxDecryptDataUtil<T> {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 加密方式
     */
    private static String AES = "AES";
    /**
     * 数据填充方式
     */
    private static String AES_CBC_PKCS7PADDING = "AES/CBC/PKCS7Padding";
    private static String AES_CBC_NoPADDING = "AES/CBC/NoPadding";
    /**
     * 避免重复new生成多个BouncyCastleProvider对象，因为GC回收不了，会造成内存溢出
     * 只在第一次调用decrypt()方法时才new 对象
     */
    private static boolean initialized = false;

    /**
     * AES解密
     * 填充模式AES/CBC/PKCS7Padding
     * 解密模式128
     *
     * @return 解密后的信息对象
     */
    public static <T> T decrypt(String encryptedData, String sessionKey, String iv,Class<T> clazz) throws Exception {
        initialize();
        try {
            Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7PADDING);
            Key sKeySpec = new SecretKeySpec(Base64.decode(sessionKey), AES);
            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIv(Base64.decode(iv)));

            byte[] data = cipher.doFinal(Base64.decode(encryptedData));
            String json = new String(data, StandardCharsets.UTF_8);
            return JsonUtils.deSerialize(json, clazz);
        } catch (Exception e) {
            throw new Exception("解密异常！");
        }
    }

    public static <T> T  rcsDecrypt(String encryptedData, String sessionKey, String iv,Class<T> clazz) throws Exception {
        try {
            byte[] encryptedDataBase64 = Base64.decode(encryptedData.getBytes());
            byte[] ivBase64 = Base64.decode(iv.getBytes());
            Cipher cipher = Cipher.getInstance(AES_CBC_NoPADDING);
            SecretKeySpec keySpec = new SecretKeySpec(Base64.decode(sessionKey.getBytes()), AES);
            IvParameterSpec ivSpec = new IvParameterSpec(ivBase64);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] original = cipher.doFinal(encryptedDataBase64);
            String originalString = new String(original);
            String json = originalString.trim();
            return JsonUtils.deSerialize(json, clazz);
        } catch (Exception e) {
            throw new Exception("解密异常！");
        }
    }

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


    /**
     * 生成iv
     *
     * @param iv
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidParameterSpecException
     */
    private static AlgorithmParameters generateIv(byte[] iv) throws NoSuchAlgorithmException, InvalidParameterSpecException {
        AlgorithmParameters params = AlgorithmParameters.getInstance(AES);
        params.init(new IvParameterSpec(iv));
        return params;
    }
}
