package com.yc.ship.module.trade.framework.common.ota.mfw;


import cn.hutool.json.JSONUtil;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;

/**
 * 说明：异常java.security.InvalidKeyException:illegal Key Size的解决方案
 * <ol>
 * <li>在官方网站下载JCE无限制权限策略文件（JDK7的下载地址：
 * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 * <li>下载后解压，可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
 * <li>如果安装了JRE，将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>
 * <li>如果安装了JDK，将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>
 * </ol>
 */
public class MfwEncrypt {

    static Charset CHARSET = Charset.forName("utf-8");
    static int BLOCK_SIZE = 32;

    /**
     * 获得对明文进行补位填充的字节.
     *
     * @param count 需要进行填充补位操作的明文字节个数
     * @return 补齐用的字节数组
     */
    public static byte[] fillByte(int count) {
        // 计算需要填充的位数
        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
        if (amountToPad == 0) {
            amountToPad = BLOCK_SIZE;
        }
        // 获得补位所用的字符
        char padChr = chr(amountToPad);
        String tmp = new String();
        for (int index = 0; index < amountToPad; index++) {
            tmp += padChr;
        }
        return tmp.getBytes(CHARSET);
    }

    /**
     * 删除解密后明文的补位字符
     *
     * @param decrypted 解密后的明文
     * @return 删除补位字符后的明文
     */
    public static byte[] removeByte(byte[] decrypted) {
        int pad = (int) decrypted[decrypted.length - 1];
        if (pad < 1 || pad > 32) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }

    /**
     * 将数字转化成ASCII码对应的字符，用于对明文进行补码
     *
     * @param a 需要转化的数字
     * @return 转化得到的字符
     */
    static char chr(int a) {
        byte target = (byte) (a & 0xFF);
        return (char) target;
    }

    /**
     * 对明文进行加密.
     *
     * @param text   需要加密的明文
     * @param aesKey 密钥
     * @return 加密后base64编码的字符串
     */
    public String encrypt(String text, String aesKey) {
        ByteGroup byteCollector = new ByteGroup();
        byte[] textBytes = text.getBytes(CHARSET);
        byteCollector.addBytes(textBytes);
        byte[] padBytes = fillByte(byteCollector.size());
        byteCollector.addBytes(padBytes);

        // 获得最终的字节流, 未加密
        byte[] unencrypted = byteCollector.toBytes();

        Base64 base64 = new Base64();
        String base64Encrypted = null;
        try {
            // 设置加密模式为AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey.getBytes(CHARSET), "AES");
            IvParameterSpec iv = new IvParameterSpec(aesKey.getBytes(CHARSET), 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

            // 加密
            byte[] encrypted = cipher.doFinal(unencrypted);

            // 使用BASE64对加密后的字符串进行编码
            base64Encrypted = base64.encodeToString(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return base64Encrypted;
    }

    /**
     * 对密文进行解密.
     *
     * @param text   需要解密的密文
     * @param aesKey 密钥
     * @return 解密得到的明文
     */
    public String decrypt(String text, String aesKey) {
        byte[] original;
        byte[] result = new byte[0];
        try {
            // 设置解密模式为AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec key_spec = new SecretKeySpec(aesKey.getBytes(CHARSET), "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey.getBytes(CHARSET), 0, 16));
            cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);

            // 使用BASE64对密文进行解码
            byte[] encrypted = Base64.decodeBase64(text);

            // 解密
            original = cipher.doFinal(encrypted);
            //去除补位
            result = removeByte(original);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new String(result, CHARSET);

    }

    class ByteGroup {
        ArrayList<Byte> byteContainer = new ArrayList<Byte>();

        public byte[] toBytes() {
            byte[] bytes = new byte[byteContainer.size()];
            for (int i = 0; i < byteContainer.size(); i++) {
                bytes[i] = byteContainer.get(i);
            }
            return bytes;
        }

        public ByteGroup addBytes(byte[] bytes) {
            for (byte b : bytes) {
                byteContainer.add(b);
            }
            return this;
        }

        public int size() {
            return byteContainer.size();
        }
    }

    public static void main(String[] args) {
        LinkedHashMap map = new LinkedHashMap();
        map.put("status", "4");
        map.put("productId", "10009");
        String jsonObject = JSONUtil.toJsonStr(map);

        String encodingAesKey = "PO4Eweg4zEOwbRzeAsToe3WlMlUbW0VN";

        String data = new MfwEncrypt().encrypt(jsonObject, encodingAesKey);
        System.out.println("加密后数据:" + data);

        String ret = new MfwEncrypt().decrypt(data, encodingAesKey);
        System.out.println("解密后数据:" + ret);
    }
}

