package com.citygis.web.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: zyl
 * @CreateTime: 2025-02-10
 * @Description: 国密
 * @Version: 1.0
 */
public class SM4Util {

    public static final String ALGORITHM_NAME = "SM4";
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";

    private final Lock lock = new ReentrantLock();
    private final Cipher cipher;
    private final SecretKey secretKey;

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

    public SM4Util(byte[] key) throws Exception {
        this.cipher = Cipher.getInstance(SM4Util.ALGORITHM_NAME_ECB_PADDING, new org.bouncycastle.jce.provider.BouncyCastleProvider());
        this.secretKey = new SecretKeySpec(key, ALGORITHM_NAME);
    }

    /**
     * 根据字符类型秘钥获取加密实例
     *
     * @param key 秘钥
     */
    public static SM4Util getInstance(String key) throws Exception {
        return new SM4Util(ByteUtils.fromHexString(key));
    }

    /**
     * 根据byte类型秘钥获取SM4加密实例
     *
     * @param key 秘钥
     */
    public static SM4Util getInstance(byte[] key) throws Exception {
        return new SM4Util(key);
    }

    private Cipher initCipher(int mode) throws InvalidKeyException {
        final Cipher cipher = this.cipher;
        cipher.init(mode, secretKey);
        return cipher;
    }


    /**
     * 加密为byte数组
     *
     * @param data 待加密byte
     */
    public byte[] encrypt_Ecb_Padding(byte[] data) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        lock.lock();
        try {
            Cipher cipher = this.initCipher(Cipher.ENCRYPT_MODE);
            return cipher.doFinal(data);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 解密为byte数组
     *
     * @param cipherText 加密后的byte数组
     */
    public byte[] decrypt_Ecb_Padding(byte[] cipherText) throws Exception {
        lock.lock();
        try {
            Cipher cipher = this.initCipher(Cipher.DECRYPT_MODE);
            return cipher.doFinal(cipherText);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 加密成Base64的密文
     *
     * @param text 待加密文字
     */
    public String encryptBase64(String text) throws Exception {
        byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);
        byte[] encode = Base64.getEncoder().encode(encrypt_Ecb_Padding(textBytes));
        return new String(encode, StandardCharsets.UTF_8);
    }

    /**
     * 解密Base64字符串的密文
     *
     * @param text 待解密文字
     */
    public String decryptBase64(String text) throws Exception {
        byte[] decode = java.util.Base64.getDecoder().decode(text);
        return new String(this.decrypt_Ecb_Padding(decode), StandardCharsets.UTF_8);
    }

    /**
     * 获取随机秘钥
     */
    public static String generateRandomKey() {
        String uuid = UUID.randomUUID().toString();
        return uuid.replace("-", "");
    }

    /**
     * 获取随机秘钥
     */
    public static String generateRandomKeyBytes() {
        String uuid = UUID.randomUUID().toString();
        return Arrays.toString(ByteUtils.fromHexString(uuid));
    }

    public static void main(String[] args) {
        String key = "77eb4c72b36644759af649d2d941c944";
        try {
            System.out.println(key);
            String pwd = "Pass@word2022#$";
            SM4Util sm4Util = SM4Util.getInstance(key);
            String encryptBase64 = sm4Util.encryptBase64(pwd);
            String decryptBase64 = sm4Util.decryptBase64(encryptBase64);
            System.out.println(encryptBase64);
            System.out.println(decryptBase64);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
