package com.haobang.util;

import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * 国密SM4加解密工具类
 * 基于 Hutool + Bouncy Castle 实现，支持 ECB 和 CBC 模式
 */
public class Sm4Util {

    /**
     * 生成 SM4 密钥（16字节，128位）
     * @return 十六进制字符串格式的密钥
     */
    public static String generateKey() {
        byte[] key = SmUtil.sm4().getSecretKey().getEncoded();
        return HexUtil.encodeHexStr(key);
    }

    // ==================== ECB 模式（电子密码本模式） ====================
    // 注意：ECB 模式不需要 IV，但安全性较低，不适合加密大量数据

    /**
     * 使用 ECB 模式加密
     * @param plainText 明文
     * @param hexKey    十六进制字符串格式的密钥
     * @return Base64 编码的密文
     */
    public static String encryptEcb(String plainText, String hexKey) {
        SM4 sm4 = new SM4(HexUtil.decodeHex(hexKey));
        return sm4.encryptBase64(plainText);
    }

    /**
     * 使用 ECB 模式解密
     * @param cipherText Base64 编码的密文
     * @param hexKey     十六进制字符串格式的密钥
     * @return 明文
     */
    public static String decryptEcb(String cipherText, String hexKey) {
        SM4 sm4 = new SM4(HexUtil.decodeHex(hexKey));
        return sm4.decryptStr(cipherText);
    }

    // ==================== CBC 模式（密码分组链接模式） ====================
    // 推荐使用：需要 IV（初始化向量），安全性更高


    static {
        // 静态代码块中统一注册 Provider，确保全局生效
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     * CBC 模式加密（推荐）
     * @param plainText 明文
     * @param hexKey    十六进制字符串格式的密钥
     * @return 格式为 "Base64(IV):Base64(密文)" 的字符串
     */
    public static String encryptCbc(String plainText, String hexKey) {
        // 1. 随机生成16字节 IV
        byte[] iv = RandomUtil.randomBytes(16);
        // 2. 将 IV 包装为 AlgorithmParameterSpec
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        // 3. 将十六进制密钥转为字节数组并包装为 SecretKey
        byte[] keyBytes = HexUtil.decodeHex(hexKey);
        SecretKey secretKey = new SecretKeySpec(keyBytes, "SM4");
        // 4. 创建加解密器
        SymmetricCrypto sm4 = new SymmetricCrypto("SM4/CBC/PKCS5Padding", secretKey, ivSpec);
        // 5. 执行加密
        String encryptedBase64 = sm4.encryptBase64(plainText);
        String ivBase64 = Base64.getEncoder().encodeToString(iv);
        return ivBase64 + ":" + encryptedBase64;
    }

    /**
     * CBC 模式解密
     * @param cipherTextWithIv 由 encryptCbc 生成的字符串
     * @param hexKey           十六进制密钥
     * @return 明文
     */
    public static String decryptCbc(String cipherTextWithIv, String hexKey) {
        String[] parts = cipherTextWithIv.split(":");
        if (parts.length != 2) {
            throw new IllegalArgumentException("无效的密文格式，应为 'IV:密文'");
        }
        // 1. 解析 IV
        byte[] iv = Base64.getDecoder().decode(parts[0]);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        // 2. 解析密钥
        byte[] keyBytes = HexUtil.decodeHex(hexKey);
        SecretKey secretKey = new SecretKeySpec(keyBytes, "SM4");
        // 3. 创建解密器（算法参数必须与加密时完全一致）
        SymmetricCrypto sm4 = new SymmetricCrypto("SM4/CBC/PKCS5Padding", secretKey, ivSpec);
        // 4. 解密
        return sm4.decryptStr(parts[1]);
    }
    public static void main(String[] args) {
        // 1. 生成 SM4 密钥（128位，十六进制字符串）
        String hexKey = Sm4Util.generateKey();
        System.out.println("生成的密钥（十六进制）: " + hexKey);
        System.out.println("密钥长度: " + HexUtil.decodeHex(hexKey).length + " 字节");

        // 2. 原始明文（包含中文和英文，测试字符集兼容性）
        String plainText = "Hello 国密算法！SM4 加解密测试。123456";
        System.out.println("原始明文: " + plainText);

        // 3. 使用 CBC 模式加密
        String encryptedWithIv = Sm4Util.encryptCbc(plainText, hexKey);
        System.out.println("加密结果（IV:密文）: " + encryptedWithIv);

        // 4. 使用 CBC 模式解密
        String decryptedText = Sm4Util.decryptCbc(encryptedWithIv, hexKey);
        System.out.println("解密后的明文: " + decryptedText);

        // 5. 验证加解密是否一致
        boolean success = plainText.equals(decryptedText);
        System.out.println("加解密验证结果: " + (success ? "成功" : " 失败"));
    }
}