package com.scm.contract.tools.sm2;

import cn.hutool.crypto.asymmetric.SM2;
import org.springframework.beans.factory.annotation.Value;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * @author zuo
 * @date 2024/6/25 15:05
 */
public class SM2Utils {

    /**
     * sm2曲线参数名称
     */
    public static final String CRYPTO_NAME_SM2 = "sm2p256v1";

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      待加密的数据
     * @return 密文，BC库产生的密文带由04标识符，与非BC库对接时需要去掉开头的04
     */
    public static String encrypt(String publicKey, String data) throws InvalidCipherTextException {
        // 按国密排序标准加密
        return encrypt(publicKey, data, SM2EngineExtend.CIPHERMODE_BC);
    }

    /**
     * SM2加密算法 （设置密文排列方式）
     *
     * @param publicKey  公钥
     * @param data       待加密的数据
     * @param cipherMode 密文排列方式0-C1C2C3；1-C1C3C2；
     * @return 密文，BC库产生的密文带由04标识符，与非BC库对接时需要去掉开头的04
     */
    public static String encrypt(String publicKey, String data, int cipherMode) throws InvalidCipherTextException {
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
        // 构造ECC算法参数，曲线方程、椭圆曲线G点、大整数N
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
        // 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        SM2EngineExtend sm2Engine = new SM2EngineExtend();
        // 设置sm2为加密模式
        sm2Engine.init(true, cipherMode, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = null;
        try {
//            byte[] in = data.getBytes();
            byte[] in = Hex.decode(data);
            arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
        } catch (Exception e) {
            throw e;
        }
        return Hex.toHexString(arrayOfBytes).toUpperCase();
    }

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return
     * @description 密文数据以04开头，传入的密文前面没有04则补上
     */
    public static String decrypt(String privateKey, String cipherData) throws InvalidCipherTextException {
        // // 按国密排序标准解密
        return decrypt(privateKey, cipherData, SM2EngineExtend.CIPHERMODE_BC);
    }

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @param cipherMode 密文排列方式0-C1C2C3；1-C1C3C2；
     * @return
     */
    public static String decrypt(String privateKey, String cipherData, int cipherMode) throws InvalidCipherTextException {
        // 使用BC库加解密时密文以04开头，传入的密文前面没有04则补上
        if (!cipherData.startsWith("04")) {
            cipherData = "04" + cipherData;
        }
        byte[] cipherDataByte = Hex.decode(cipherData);

        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        SM2EngineExtend sm2Engine = new SM2EngineExtend();
        // 设置sm2为解密模式
        sm2Engine.init(false, cipherMode, privateKeyParameters);

        String result = "";
        try {
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
//            return new String(arrayOfBytes);

            return Hex.toHexString(arrayOfBytes).toUpperCase();
//            return Util.byteToHex(arrayOfBytes);
        } catch (Exception e) {
            throw e;
        }


    }

    /**
     * SM2 加签
     *
     * @param privateKey 承建商私钥
     * @param id         授权码
     * @param source     16进制加密原文
     * @return 16进制签名
     */

    public static String sign(String privateKey, String id, String source) throws UnsupportedEncodingException {
        SM2 sm2 = new SM2(privateKey, null);
        String id16 = Hex.toHexString(id.getBytes("utf-8"));
        String source16 = Hex.toHexString(source.getBytes("utf-8"));

        String sign = sm2.signHex(source16, id16);
        return sign;
    }

    /**
     * 校验签名
     *
     * @param publicKey 民科提供公钥
     * @param data      16进制加密原文
     * @param signHex   16进制签名
     * @param id        授权码
     * @return
     */
    public static boolean verifysign(String publicKey, String data, String signHex, String id) throws UnsupportedEncodingException {
        SM2 sm = new SM2(null, publicKey);
        String id16 = Hex.toHexString(id.getBytes("utf-8"));
        String source16 = Hex.toHexString(data.getBytes("utf-8"));
        boolean isSign = sm.verifyHex(data, signHex, id);
        return isSign;
    }

//    public static void main(String[] args)  {
//        try {
//            String publicKey = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
//            String privateKey = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
//            String data = "0A05F60F2D6BC70103EB5BC70C";
//            int cipherMode = 0;
//            String cipherData = encrypt(publicKey, data, cipherMode);
////        String cipherData = "04AA3308E72D18035CB23E58815C6649C91AB3A6DB5F60555D4E056E70B7AE1375E99949C597313DF557B484165CC58D125A45EE0AC222D7123C758F322E8F29276B50891F62EC822CC6DCE07CE71018835B89EC2A38C183090759D471CD055C1FF0235840EE170CDD5E476D4E27";
//            System.out.println("加密后：" + cipherData);
//            String decrypt = decrypt(privateKey, cipherData, cipherMode);
//            System.out.println("解密：" + decrypt);
//        } catch (InvalidCipherTextException e) {
//            throw new RuntimeException(e);
//        }
//    }
}
