package com.cusc.nirvana.user.rnr.fp.common.cloud;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Base64;

/**
 * Description: 国密SM4
 * <br />
 * BC库从1.59版本开始已经基本实现了国密算法（SM2、SM3、SM4）
 **/
public class Sm4DemoUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(Sm4DemoUtils.class);

    //算法名称
    public static final String ALGORITHM_NAME = "SM4";
    //ECB P5填充
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private Sm4DemoUtils() {
    }

    /**
     * ecb加密默认 hexString true
     **/
    public static String encryptEcbPaddingHexString(String key, String data) {
        return encryptEcbPaddingHex(key, data, true, null);
    }

    /**
     * ecb解密默认 hexString true
     **/
    public static String decryptEcbPaddingString(String key, String data) {
        return decryptEcbPaddingHex(key, data, true, null);
    }

    /**
     * Description: ecb解密，可以指定混淆
     * <br />
     * CreateDate 2021-11-17 15:53:34
     *
     * @author yuyi
     **/
    public static String decryptEcbPaddingHex(String key, String data, boolean hexString, String charset) {
        if (StringUtils.isEmpty(data)) {
            return null;
        }
        try {
            byte[] keyBytes;
            if (hexString) {
                keyBytes = hexStringToBytes(key);
            } else {
                keyBytes = key.getBytes();
            }
            byte[] decrypted = decryptEcbPaddingByte(keyBytes, base64Decoder(data));
            if (StringUtils.isNotEmpty(charset)) {
                return new String(decrypted, charset);
            }
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            LOGGER.error("Sm4Util.decryptEcbPaddingHex error ! ", e);
            return null;
        }
    }


    /**
     * ECB P5填充解密
     *
     * @param key        密钥
     * @param cipherText 加密后的数据
     * @return byte
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     */
    private static byte[] decryptEcbPaddingByte(byte[] key, byte[] cipherText)
            throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * Description: ecb加密，可以指定混淆
     * <br />
     * CreateDate 2021-11-17 15:53:34
     *
     * @author yuyi
     **/
    private static String encryptEcbPaddingHex(String key, String data, boolean hexString, String charset) {
        if (StringUtils.isEmpty(data)) {
            return null;
        }
        try {
            byte[] keyBytes;
            if (hexString) {
                keyBytes = hexStringToBytes(key);
            } else {
                keyBytes = key.getBytes();
            }
            byte[] dataBytes;
            if (StringUtils.isNotEmpty(charset)) {
                dataBytes = data.getBytes(charset);
            } else {
                dataBytes = data.getBytes(StandardCharsets.UTF_8);
            }
            byte[] encrypted = encryptEcbPaddingByte(keyBytes, dataBytes);
            return base64Encoder(encrypted);
        } catch (Exception e) {
            LOGGER.error("Sm4Util.encryptEcbPaddingHex error ! ", e);
            return null;
        }
    }

    /**
     * base64编码
     **/
    private static String base64Encoder(byte[] encrypted) {
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * base64解码
     **/
    private static byte[] base64Decoder(String encrypted) throws IOException {
        return Base64.getDecoder().decode(encrypted);
    }
    /**
     * ECB P5填充加密
     *
     * @param key  密钥
     * @param data 明文数据
     * @return byte
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    private static byte[] encryptEcbPaddingByte(byte[] key, byte[] data)
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * ECB P5填充加解密Cipher初始化
     *
     * @param algorithmName 算法名称
     * @param mode          1 加密  2解密
     * @param key           密钥
     * @return Cipher
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key)
            throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }


    /**
     * Convert hex string to byte[]
     *
     * @param hexString
     *            the hex string
     * @return byte[]
     */
    public static byte[] hexStringToBytes(String hexString)
    {
        if (hexString == null || hexString.equals(""))
        {
            return null;
        }

        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++)
        {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    /**
     * Convert char to byte
     *
     * @param c
     *            char
     * @return byte
     */
    public static byte charToByte(char c)
    {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
}
