package com.ruoyi.common.third.utils;

import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECFieldElement.Fp;
import org.bouncycastle.math.ec.ECPoint;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;

/**
 * 类的用途：SM2 算法核心业务<p>
 * @version 1.0
 */
public class SM2 {


    // 正式参数
    public static String[] ecc_param = { "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
            "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
            "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
            "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
            "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
            "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" };

    /**
     * 单例
     */
    private static SM2 sm2_entity;
  
    public final BigInteger ecc_p;

    public final BigInteger ecc_a;

    public final BigInteger ecc_b;

    public final BigInteger ecc_n;

    public final BigInteger ecc_gx;

    public final BigInteger ecc_gy;

    public final ECCurve ecc_curve;

    public final ECPoint ecc_point_g;

    public final ECDomainParameters ecc_bc_spec;

    public final ECKeyPairGenerator ecc_key_pair_generator;

    public final ECFieldElement ecc_gx_fieldelement;

    public final ECFieldElement ecc_gy_fieldelement;

    /**
     * 单例模式
     * @author: WangBinBin
     * @return
     */
    public static SM2 Instance() {
        if (null == SM2.sm2_entity) {
            SM2.sm2_entity = new SM2();
        }
        return SM2.sm2_entity;
    }

    private SM2() {
        this.ecc_p = new BigInteger(ecc_param[0], 16);
        this.ecc_a = new BigInteger(ecc_param[1], 16);
        this.ecc_b = new BigInteger(ecc_param[2], 16);
        this.ecc_n = new BigInteger(ecc_param[3], 16);
        this.ecc_gx = new BigInteger(ecc_param[4], 16);
        this.ecc_gy = new BigInteger(ecc_param[5], 16);

        //bcprov-jdk16升级成bcprov-jdk15on升级后修改
       /* this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
        this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);*/
        //创建椭圆曲线 /bcprov-jdk16升级成bcprov-jdk15on升级后修改
//        this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b, this.ecc_n, BigInteger.ONE);
        //使用SM2标准曲线 bcprov-jdk16升级成bcprov-jdk15on升级后修改 SM2国家标准规定的曲线名称
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
        this.ecc_curve = spec.getCurve();
        this.ecc_gx_fieldelement = ecc_curve.fromBigInteger(this.ecc_gx);
        this.ecc_gy_fieldelement = ecc_curve.fromBigInteger(this.ecc_gy);
//        this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
//        this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);

        //创建基点 G , bcprov-jdk16升级成bcprov-jdk15on后修改
        this.ecc_point_g = ecc_curve.createPoint(this.ecc_gx, this.ecc_gy).normalize();
//        ecc_curve.createPoint(ecc_gx, ecc_gy);
        //配置域参数
        this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);

        ECKeyGenerationParameters ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());

        this.ecc_key_pair_generator = new ECKeyPairGenerator();
        this.ecc_key_pair_generator.init(ecc_ecgenparam);
    }

    public byte[] sm2GetZ(byte[] userId, ECPoint userKey) {
        SM3Util sm3 = new SM3Util();

        int len = userId.length * 8;
        sm3.update((byte) (len >> 8 & 0xFF));
        sm3.update((byte) (len & 0xFF));
        sm3.update(userId, 0, userId.length);

        byte[] p = CipherUtil.byteConvert32Bytes(ecc_a);
        sm3.update(p, 0, p.length);

        p = CipherUtil.byteConvert32Bytes(ecc_b);
        sm3.update(p, 0, p.length);

        p = CipherUtil.byteConvert32Bytes(ecc_gx);
        sm3.update(p, 0, p.length);

        p = CipherUtil.byteConvert32Bytes(ecc_gy);
        sm3.update(p, 0, p.length);

        //20250515 升级到bcprov-jdk15on
//        p = CipherUtil.byteConvert32Bytes(userKey.getX().toBigInteger());
        p = CipherUtil.byteConvert32Bytes(userKey.getAffineXCoord().toBigInteger());
        sm3.update(p, 0, p.length);

        p = CipherUtil.byteConvert32Bytes(userKey.getAffineYCoord().toBigInteger());
        sm3.update(p, 0, p.length);

        byte[] md = new byte[sm3.getDigestSize()];
        sm3.doFinal(md, 0);
        return md;
    }

    /**
     * SM2签名
     * @author: WangBinBin
     * @param md 参数 md
     * @param userD 参数userD
     * @param userKey 参数 userKey
     * @param sm2Result 参数 sm2Result
     */
    public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result) {
        BigInteger e = new BigInteger(1, md);
        BigInteger k = null;
        ECPoint kp = null;
        BigInteger r = null;
        BigInteger s = null;
        do {
            do {
                // 生成随机数k
                byte[] rand = new byte[33];
                Random ra = new Random();
                ra.nextBytes(rand);
                rand[0] = 0x00;

                String kS = CipherUtil.byteToHex(rand);
                k = new BigInteger(kS, 16);
                kp = this.ecc_point_g.multiply(k).normalize();
//                r = e.add(kp.getX().toBigInteger());
                r = e.add(kp.getAffineXCoord().toBigInteger());
                r = r.mod(ecc_n);
            } while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));

            // (1 + dA)~-1
            BigInteger da_1 = userD.add(BigInteger.ONE);
            da_1 = da_1.modInverse(ecc_n);

            // s
            s = r.multiply(userD);
            s = k.subtract(s).mod(ecc_n);
            s = da_1.multiply(s).mod(ecc_n);
        } while (s.equals(BigInteger.ZERO));
        sm2Result.r = r;
        sm2Result.s = s;
    }

    /**
     * SM2 验证签名
     * @author: WangBinBin
     * @param md  参数 md
     * @param userKey 参数userKey
     * @param r 参数r
     * @param s 参数s
     * @param sm2Result 参数sm2Result
     */
    public void sm2Verify(byte md[], ECPoint userKey, BigInteger r, BigInteger s, SM2Result sm2Result) {
        sm2Result.R = null;
        BigInteger e = new BigInteger(1, md);
        BigInteger t = r.add(s).mod(ecc_n);
        if (t.equals(BigInteger.ZERO)) {
            return;
        } else {
            ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s).normalize();
            x1y1 = x1y1.add(userKey.multiply(t).normalize()).normalize();
//            sm2Result.R = e.add(x1y1.getX().toBigInteger()).mod(ecc_n);
            sm2Result.R = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(ecc_n);
            return;
        }
    }
}
