package cn.gintone.encryptionUtils;


import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.math.ec.ECPoint;

import java.security.*;
import java.util.Base64;


public class SM2Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    // SM2加密方法
    public static byte[] sm2Encrypt(byte[] data, PublicKey publicKey) throws Exception {
        SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey);
        engine.init(true, new ParametersWithRandom(ecPublicKey, new SecureRandom()));
        return engine.processBlock(data, 0, data.length);
    }

    // SM2解密方法
    public static byte[] sm2Decrypt(byte[] cipherText, PrivateKey privateKey) throws Exception {
        SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        ECPrivateKeyParameters ecPrivateKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
        engine.init(false, ecPrivateKey);
        return engine.processBlock(cipherText, 0, cipherText.length);
    }

    // 生成SM2密钥对
    public static KeyPair generateSm2KeyPair() throws Exception {
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
        keyPairGenerator.initialize(ecSpec, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    // 将公钥转换为字节数组
    public static byte[] publicKeyToBytes(PublicKey publicKey) {
        return ((org.bouncycastle.jce.interfaces.ECPublicKey) publicKey).getQ().getEncoded(true);
    }

    // 将字节数组转换为公钥
    public static PublicKey bytesToPublicKey(byte[] keyBytes) throws Exception {
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        ECPoint ecPoint = ecSpec.getCurve().decodePoint(keyBytes);
        ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, ecSpec);
        return KeyFactory.getInstance("EC", "BC").generatePublic(keySpec);
    }

    // 将私钥转换为字节数组
    public static byte[] privateKeyToBytes(PrivateKey privateKey) {
        return ((org.bouncycastle.jce.interfaces.ECPrivateKey) privateKey).getD().toByteArray();
    }

    // 将字节数组转换为私钥
    public static PrivateKey bytesToPrivateKey(byte[] keyBytes) throws Exception {
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        ECPrivateKeySpec keySpec = new ECPrivateKeySpec(new java.math.BigInteger(1, keyBytes), ecSpec);
        return KeyFactory.getInstance("EC", "BC").generatePrivate(keySpec);
    }

    public static void main(String[] args) throws Exception {
        // 生成密钥对
        KeyPair keyPair = generateSm2KeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        String pubStr = SM2KeyUtils.publicKeyToString(publicKey);
        String priStr = SM2KeyUtils.privateKeyToString(privateKey);


        PublicKey publicKey1 = SM2KeyUtils.stringToPublicKey(pubStr);
        PrivateKey privateKey1 = SM2KeyUtils.stringToPrivateKey(priStr);
        // 测试数据
        String originalText = "Hello, SM2加密测试!";
        for (int i = 0; i < 1000; i ++) {

            originalText += "加密测试加密测试加密测试加密测试加密测试加密测试sadfasdfasdfaf";
        }
        byte[] data = originalText.getBytes("UTF-8");
        // 加密
        byte[] encryptedData = sm2Encrypt(data, publicKey1);
        System.out.println("加密结果 (Base64): " + Base64.getEncoder().encodeToString(encryptedData));

        // 解密
        byte[] decryptedData = sm2Decrypt(encryptedData, privateKey1);
        String decryptedText = new String(decryptedData, "UTF-8");
        System.out.println("解密结果: " + decryptedText);
    }
}
