package com.oraclechain.pocketeos.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.util.encoders.Hex;

import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Created by pocketEos on 2018/5/26.
 * 加密工具类
 */
public class EncryptUtil {


    /**
     * The constant KEY_ALGORITHM.
     */
    static final String KEY_ALGORITHM = "AES";
    /**
     * The constant algorithmStr.
     */
    static final String algorithmStr = "AES/CBC/PKCS7Padding";
    /**
     * The Iv.
     */
    static byte[] iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    //
    private static Key key;
    private static Cipher cipher;

    /**
     * Gets encrypt string.
     *加密
     * @param content  the content
     * @param password the password
     * @return the encrypt string
     * @throws NoSuchAlgorithmException the no such algorithm exception
     * @throws InvalidKeySpecException  the invalid key spec exception
     */
    public static String getEncryptString(String content, String password)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        String salt = getRandomString(32);//盐
        char[] chars = password.toCharArray();//加密密码明文

        byte[] encryPassword = pbkdf2(chars, salt.getBytes(), 1000, 32);
        // 加密
        byte[] enc = encrypt(content.getBytes(), encryPassword);
        return salt + new String(Hex.encode(enc));
    }

    /**
     * Gets decrypt string.
     *解密
     * @param content  the content
     * @param password the password
     * @return the decrypt string
     * @throws NoSuchAlgorithmException the no such algorithm exception
     * @throws InvalidKeySpecException  the invalid key spec exception
     */
    public static String getDecryptString(String content, String password)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        String  salt = content.substring(0,32);//盐
        char[] chars = password.toCharArray();//加密密码明文

        byte[] encryPassword = pbkdf2(chars, salt.getBytes(), 1000, 32);
        // 解密
        String dncstr =content.substring(32 , content.length());
        byte[] dec = decrypt(Hex.decode(dncstr), encryPassword);
        return new String(dec);
    }

    /**
     * Gets random string.
     *
     * @param length the length
     * @return random string
     */
    public static String getRandomString(int length) { //length表示生成字符串的长度
        String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * Computes the PBKDF2 hash of a password.
     *
     * @param password   the password to hash.   需要加密的明文密码
     * @param salt       the salt                盐增加调味 增加密码破解难度
     * @param iterations the iteration count (slowness factor)  迭代次数
     * @param bytes      the length of the hash to compute in bytes  计算密码后的 哈希长度
     * @return the PBDKF2 hash of the password
     * @throws NoSuchAlgorithmException the no such algorithm exception
     * @throws InvalidKeySpecException  the invalid key spec exception
     */
    public static byte[] pbkdf2(char[] password, byte[] salt,
                                int iterations, int bytes)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        return skf.generateSecret(spec).getEncoded();
    }

    /**
     * 加密方法
     *
     * @param content  要加密的字符串
     * @param keyBytes 加密密钥
     * @return byte [ ]
     */
    public static byte[] encrypt(byte[] content, byte[] keyBytes) {
        byte[] encryptedText = null;
        init(keyBytes);
        try {
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
            encryptedText = cipher.doFinal(content);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return encryptedText;
    }

    /**
     * Init.
     *
     * @param keyBytes the key bytes
     */
    public static void init(byte[] keyBytes) {

        // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
        int base = 16;
        if (keyBytes.length % base != 0) {
            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
            keyBytes = temp;
        }
        // 初始化
        Security.addProvider(new BouncyCastleProvider());
        // 转化成JAVA的密钥格式
        key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        try {
            // 初始化cipher
            cipher = Cipher.getInstance(algorithmStr, "BC");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 解密方法
     *
     * @param encryptedData 要解密的字符串
     * @param keyBytes      解密密钥
     * @return byte [ ]
     */
    public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes) {
        byte[] encryptedText = null;
        init(keyBytes);
        try {
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
            encryptedText = cipher.doFinal(encryptedData);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return encryptedText;
    }
}