package com.ruoyi.gysl.login.util;

import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.engines.SM2Engine.Mode;
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.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SM2Demo {
    /**
     * 国密SM2加密算法，对标RSA
     */

    private static final String ALGORITHM = "SM2";
    private static final String SM2 = "EC";
    private static final String C1C2C3 = "C1C2C3";


    /**
     * 加密方法
     * @param objects
     * @return
     * @throws Exception
     */

    public static Object exec(Object... objects) throws Exception {
        if (objects == null || objects.length != 3) {
            return null;
        }
        byte[] message = (byte[]) objects[0];
        String order = objects[1].toString();
        byte[] publicKey = (byte[]) objects[2];
        ECPublicKey ecPublicKey = (ECPublicKey) KeyFactory.getInstance(SM2, new BouncyCastleProvider())
                .generatePublic(new X509EncodedKeySpec(publicKey));
        ECPublicKeyParameters ecPublicKeyParameters = getPublicKeyParameters(ecPublicKey);
        Mode mode = order.equals(C1C2C3) ? Mode.C1C2C3 : Mode.C1C3C2;
        SM2Engine sm2Engine = new SM2Engine(mode);
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters));
        return sm2Engine.processBlock(message, 0, message.length);
    }
    /**
     * 解密方法
     * @param objects
     * @return
     * @throws Exception
     */
    public static Object dexec(Object... objects) throws Exception {
        if (objects == null || objects.length != 3) {
            return null;
        }
        byte[] message = (byte[]) objects[0];
        String order = objects[1].toString();
        byte[] privateKey = (byte[]) objects[2];

        ECPrivateKey ecPrivateKey = (ECPrivateKey) KeyFactory.getInstance(SM2, new BouncyCastleProvider())
                .generatePrivate(new PKCS8EncodedKeySpec(privateKey));
        ECPrivateKeyParameters privateKeyParameters = getPrivateKeyParameters(ecPrivateKey);
        Mode mode = order.equals(C1C2C3) ? Mode.C1C2C3 : Mode.C1C3C2;
        SM2Engine sm2Engine = new SM2Engine(mode);
        sm2Engine.init(false, privateKeyParameters);
        return sm2Engine.processBlock(message, 0, message.length);
    }

    private static ECPublicKeyParameters getPublicKeyParameters(ECPublicKey ecPublicKey) {
        ECParameterSpec parameterSpec = ecPublicKey.getParameters();
        ECCurve curve = parameterSpec.getCurve();
        ECPoint g = parameterSpec.getG();
        BigInteger n = parameterSpec.getN();
        BigInteger h = parameterSpec.getH();
        ECDomainParameters domainParams = new ECDomainParameters(curve, g, n, h);
        return new ECPublicKeyParameters(ecPublicKey.getQ(), domainParams);
    }

    private static ECPrivateKeyParameters getPrivateKeyParameters(ECPrivateKey ecPrivateKey) {
        ECParameterSpec parameterSpec = ecPrivateKey.getParameters();
        ECCurve curve = parameterSpec.getCurve();
        ECPoint g = parameterSpec.getG();
        BigInteger n = parameterSpec.getN();
        BigInteger h = parameterSpec.getH();
        ECDomainParameters domainParams = new ECDomainParameters(curve, g, n, h);
        return new ECPrivateKeyParameters(ecPrivateKey.getD(), domainParams);
    }

//    public static void main(String[] args) throws Exception {
//        SM2Demo computeFactor = new SM2Demo();
//        String pub = "XXXX";//公钥
//        String pri = "XXXX";//私钥
//        //待加密字符串
//        String text = "XXXXXXXXXXXXXX";
//        Object result = computeFactor
//                .exec(text.getBytes(StandardCharsets.UTF_8), "C1C2C3", Base64.getDecoder().decode(pub));
//        System.out.println("加密: " + Base64.getEncoder().encodeToString((byte[]) result));
//        //待解密base64编码
//        String encode = "XXXXXXXXXXXXX";
//        Object result1 = computeFactor.dexec(Base64.getDecoder().decode(encode), "C1C2C3", Base64.getDecoder().decode(pri));
//        System.out.println("解密: " + new String((byte[]) result1, StandardCharsets.UTF_8));
//    }
}
