package com.fs.baidu.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;

public class AESUtils {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 将字符串 keyString 的AES秘钥转换成SecretKey对象
     *
     * @param keyString
     * @return
     * @throws Exception
     */
    public static SecretKey loadKeyAES(String keyString) throws Exception {
        return new SecretKeySpec(keyString.getBytes(StandardCharsets.UTF_8), "AES");
    }

    /**
     * 加密
     *
     * @param source
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptAES(byte[] source, SecretKey key, String vectorKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes(StandardCharsets.UTF_8));
        int dataLength = source.length;
        if (dataLength % 16 != 0) {
            dataLength = dataLength + (16 - (dataLength % 16));
        }
        byte[] paddingBytes = new byte[dataLength];
        System.arraycopy(source, 0, paddingBytes, 0, source.length);
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);
        byte[] encryped = cipher.doFinal(paddingBytes);
        return bin2HexStr(encryped);
    }

    /**
     * 加密
     *
     * @param source
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptAES(byte[] source, SecretKey key) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        int dataLength = source.length;
        if (dataLength % 16 != 0) {
            dataLength = dataLength + (16 - (dataLength % 16));
        }
        byte[] paddingBytes = new byte[dataLength];
        System.arraycopy(source, 0, paddingBytes, 0, source.length);
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] encryped = cipher.doFinal(paddingBytes);
        return Base64.getEncoder().encodeToString(encryped);
    }

    /**
     * 解密
     *
     * @param encrypedStr
     * @param key
     * @return
     * @throws Exception
     */
    public static String decryptAES(String encrypedStr, SecretKey key) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        byte[] source = Base64.getDecoder().decode(encrypedStr);
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] retBytes = cipher.doFinal(source);
        return new String(retBytes, StandardCharsets.UTF_8);
    }

    /**
     * 解密
     *
     * @param encrypedStr
     * @param key
     * @return
     * @throws Exception
     */
    public static String decryptAES(String encrypedStr, SecretKey key, String vectorKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes());
        byte[] source = hexStringToByte(encrypedStr);
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        byte[] retBytes = cipher.doFinal(source);
        return new String(retBytes, StandardCharsets.UTF_8);
    }

    /**
     * @param bytes
     * @return 将二进制数组转换为十六进制字符串  2-16
     */
    public static String bin2HexStr(byte[] bytes) {
        String hexStr = "0123456789ABCDEF";
        StringBuilder result = new StringBuilder();
        String hex = "";
        for (int i = 0; i < bytes.length; i++) {
            // 字节高4位
            hex = String.valueOf(hexStr.charAt((bytes[i] & 0xF0) >> 4));
            // 字节低4位
            hex += String.valueOf(hexStr.charAt(bytes[i] & 0x0F));
            result.append(hex);
        }
        return result.toString();
    }

    private static int parse(char c) {
        if (c >= 'a') {
            return (c - 'a' + 10) & 0x0f;
        }
        if (c >= 'A') {
            return (c - 'A' + 10) & 0x0f;
        }
        return (c - '0') & 0x0f;
    }

    public static byte[] hexStringToByte(String hex) {
        byte[] b = new byte[hex.length() / 2];
        int j = 0;
        for (int i = 0; i < b.length; i++) {
            char c0 = hex.charAt(j++);
            char c1 = hex.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }
}