package cn.pluss.platform.util;

import com.alipay.api.internal.util.codec.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;

/**
 * @author ryx
 * AES加密工具类
 */
public class AESEncrypt {

    public static final String KEY_ALGORITHM_PADDING = "AES/CBC/PKCS5Padding";
    private static final String KEY_ALGORITHM = "AES";
    public static SecureRandom random = new SecureRandom();

    public static final String KEY_ALGORITHM_ECB_PADDING = "AES/ECB/PKCS5Padding";

    /**
     * 生成二进制密钥
     *
     * @return
     * @throws Exception
     */
    public static byte[] initKey() throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
        kg.init(256);
        return kg.generateKey().getEncoded();
    }

    /**
     * 数据加密
     *
     * @param data    待加密数据
     * @param key     密钥
     * @param ivParam 向量
     * @return 加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, byte[] key, String ivParam) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
		// 创建密码器
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING);
        if (StringUtils.trimToNull(ivParam) != null) {
            //使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(ivParam.getBytes());
            // 初始化
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        }
		// 加密
		return cipher.doFinal(data);
    }

    public static byte[] encryptEcb(byte[] data, byte[] key, String ivParam) throws Exception {
        // 创建密码器
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_ECB_PADDING);
        SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
        if (StringUtils.trimToNull(ivParam) != null) {
            //使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(ivParam.getBytes());
            // 初始化
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        } else {
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        }
        // 加密
        return cipher.doFinal(data);
    }

    /**
     * 数据加密
     *
     * @param data    待加密数据
     * @param key     密钥
     * @param ivParam 向量
     * @return 加密数据
     * @throws Exception
     */
    public static String encryptToString(byte[] data, byte[] key, String ivParam) throws Exception {
        return Base64.encodeBase64String(encrypt(data, key, ivParam));
    }

    public static String encryptEcbToString(byte[] data, byte[] key, String ivParam) throws Exception {
        return Base64.encodeBase64String(encryptEcb(data, key, ivParam));
    }

    /**
     * 数据解密
     *
     * @param data    待解密数据
     * @param key     密钥
     * @param ivParam 向量
     * @return 解密数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, byte[] key, String ivParam) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING);// 创建密码器
        IvParameterSpec iv = new IvParameterSpec(ivParam.getBytes());//使用CBC模式，需要一个向量iv，可增加加密算法的强度
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);// 初始化
        byte[] result = cipher.doFinal(data);
        return result; // 加密
    }

    /**
     * 数据加密钥
     *
     * @param data    待解密数据
     * @param key     密钥
     * @param ivParam 向量
     * @return 解密数据
     * @throws Exception
     */
    public static String decryptToString(byte[] data, byte[] key, String ivParam) throws Exception {
        return new String(decrypt(data, key, ivParam), "utf-8");
    }

    /**
     * 数据加密钥
     *
     * @param data    待解密数据
     * @param key     密钥
     * @param ivParam 向量
     * @return 解密数据
     * @throws Exception
     */
    public static String decryptEcbToString(byte[] data, byte[] key, String ivParam) throws Exception {
        return new String(decryptEcb(data, key, ivParam), StandardCharsets.UTF_8);
    }

    private static byte[] decryptEcb(byte[] encryptedBytes, byte[] keyBytes, String IV) throws Exception{
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_ECB_PADDING);
        SecretKey secretKey = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        if (IV != null && StringUtils.trimToNull(IV) != null) {
            IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
        } else {
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
        }
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return decryptedBytes;
    }


    /**
     * @param length
     * @return
     */
    public static String getIVParam(int length) {
        StringBuilder ret = new StringBuilder();
        for (int i = 0; i < length; i++) {
            boolean isChar = (random.nextInt(2) % 2 == 0);// 输出字母还是数字
            if (isChar) { // 字符串
                int choice = random.nextInt(2) % 2 == 0 ? 65 : 97; // 取得大写字母还是小写字母
                ret.append((char) (choice + random.nextInt(26)));
            } else { // 数字
                ret.append(Integer.toString(random.nextInt(10)));
            }
        }
        return ret.toString();
    }

    public static String decryptEcb(String content, String sKey) {
        try {
            //实例化
             Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_ECB_PADDING);
             cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sKey.getBytes(StandardCharsets.UTF_8), "AES"));
             byte[] encrypted = java.util.Base64.getDecoder().decode(content); // 先用base64解密
             byte[] original = cipher.doFinal(encrypted);
             return new String(original, StandardCharsets.UTF_8);
         } catch (Exception ex) {
            return null;
        }
    }

//    public static void main(String[] args) {
//        String aa = "name=123&age=234";
//        try {
//            String p = AESEncrypt.encryptDefaultToString(aa);
//            System.out.println("p=>"+p);
//            String s = Base64.encodeBase64String(p.getBytes(StandardCharsets.UTF_8));
//            System.out.println("s=>"+s);
//            byte[] bytes = Base64.decodeBase64String(s);
//            String str = new String(bytes, "UTF-8");
//            System.out.println("=======str=>"+str);
//            String cc = AESEncrypt.decryptDefaultToString(str);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

//    public static void main(String[] args) {
//        String aa = "name=123&age=234";
//        try {
//            String pa = AESEncrypt.encryptDefaultBaseToString(aa);
//            System.out.println("pa=>"+pa);
//            String cp = StringUtil.base64Encoder(pa);
//            System.out.println("cp=>"+cp);
//
//            String de = StringUtil.base64Decoder(cp);
//            System.out.println("de=>"+de);
//            String cd = decryptDefaultBaseToString(de);
//            System.out.println("cd=>"+cd);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
}
