SM2代码(Java&&TS)

Java

public class SM2Util {

        private SM2Util() {

        }

        /**
         * p=FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
         * a=FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
         * b=28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
         * n=FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
         * Gx=32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
         * Gy=BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
         */

        // 8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3
        private static final BigInteger ECC_P = new BigInteger(
                        "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16);
        // 787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498
        private static final BigInteger ECC_A = new BigInteger(
                        "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16);
        // 63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A
        private static final BigInteger ECC_B = new BigInteger(
                        "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16);
        // 8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7
        private static final BigInteger ECC_N = new BigInteger(
                        "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16);
        // 421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D
        private static final BigInteger ECC_X = new BigInteger(
                        "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
        // 0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2
        private static final BigInteger ECC_Y = new BigInteger(
                        "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);

        private static final BigInteger[] ECC_POINT = new BigInteger[] { ECC_X, ECC_Y };

        private static final Random random = new Random();
        private static final BigInteger THREE = BigInteger.valueOf(3);

        /**
         * 密钥对生成
         * 
         * 密钥对的生成
         * 输入:一个有效的Fq(q = p且p为大于3的素数,或q = m)上椭圆曲线系统参数的集合。
         * 输出:与椭圆曲线系统参数相关的一个密钥对(d,P)。
         * a) 用随机数发生器产生整数d ∈ [1,n−2];
         * b) G为基点,计算点P = (xP,yP) = [d]G;(参见附录A.3.2。)
         * c) 密钥对是(d,P),其中d为私钥,P为公钥。
         * 
         * @return
         */
        public static String[] createkey() {

                BigInteger privateKey;
                do {
                        // 生成私钥
                        privateKey = new BigInteger(ECC_N.bitLength(), random);

                } while (privateKey.compareTo(BigInteger.ONE) <= 0
                                || privateKey.compareTo(ECC_N) > 0);
                BigInteger[] multiPoint = multiPoints(ECC_POINT, privateKey);

                return new String[] { ArrayUtils.bytesToHex(privateKey.toByteArray()),
                                ArrayUtils.bytesToHex(multiPoint[0].toByteArray()) +
                                                ArrayUtils.bytesToHex(multiPoint[1].toByteArray()) };
        }

        /**
         * 椭圆曲线多倍点运算的实现
         * 
         * @param pointOnes
         * @param pointTwo
         * @return
         */
        private static BigInteger[] multiPoints(BigInteger[] pointOnes, BigInteger pointTwo) {
                BigInteger point = pointTwo;
                // 初始化结果
                BigInteger[] multiPoint = { BigInteger.ZERO, BigInteger.ZERO };
                BigInteger[] pointOne = new BigInteger[] { pointOnes[0], pointOnes[1] };

                int i = 0;
                // 判断数据是否为0
                while (BigInteger.ZERO.compareTo(point) < 0) {
                        // 判断取模是否等于1
                        if (point.mod(BigInteger.TWO).equals(BigInteger.ONE)) {

                                addPoint(multiPoint, pointOne);
                        }
                        BigInteger temp = (pointOne[0].pow(2).multiply(THREE)).add(ECC_A)
                                        .multiply(pointOne[1].multiply(BigInteger.TWO)
                                                        .modPow(ECC_P.subtract(BigInteger.TWO), ECC_P));

                        BigInteger x = (temp.pow(2).subtract(pointOne[0].multiply(BigInteger.TWO))).mod(ECC_P);
                        pointOne[1] = (temp.multiply(pointOne[0].subtract(x)).subtract(pointOne[1]))
                                        .mod(ECC_P);
                        pointOne[0] = x;
                        point = point.divide(BigInteger.TWO);
                }

                return multiPoint;
        }

        /**
         * 两个点位相加
         * 
         * @param pointTwo
         * @param multiPoint
         * @return
         */
        private static void addPoint(BigInteger[] pointOne, BigInteger[] pointTwo) {

                if ((BigInteger.ZERO.equals(pointTwo[0]) && BigInteger.ZERO.equals(pointTwo[1]))
                                || (BigInteger.ZERO.equals(pointOne[0]) && BigInteger.ZERO.equals(pointOne[1]))) {
                        pointOne[0] = pointOne[0].add(pointTwo[0]);
                        pointOne[1] = pointOne[1].add(pointTwo[1]);
                } else {

                        BigInteger temp = pointOne[1].subtract(pointTwo[1])
                                        .multiply((pointOne[0].subtract(pointTwo[0]))
                                                        .modPow(ECC_P.subtract(BigInteger.TWO), ECC_P));

                        BigInteger x = (temp.pow(2).subtract(pointOne[0]).subtract(pointTwo[0])).mod(ECC_P);
                        pointOne[1] = (temp.multiply(pointOne[0].subtract(x))).subtract(pointOne[1])
                                        .mod(ECC_P);
                        pointOne[0] = x;
                }

        }

        /**
         * 验证公钥P是否有效
         * 
         * @param a  椭圆曲线参数a
         * @param b  椭圆曲线参数b
         * @param p  素数p
         * @param xP 公钥P的x坐标
         * @param yP 公钥P的y坐标
         * @return 如果公钥P有效,则返回"有效";否则返回"无效"
         */
        public boolean validatePublicKey(BigInteger a, BigInteger b, BigInteger p, BigInteger xP, BigInteger yP) {
                // 验证P不是无穷远点O
                if (xP.equals(BigInteger.ZERO) && yP.equals(BigInteger.ZERO)) {
                        return false;
                }

                // 验证公钥P的坐标xp和yp是域F中的元素
                if (!xP.mod(p).equals(xP) || !yP.mod(p).equals(yP)) {
                        return false;
                }

                // 计算号=xp+axp+b(modp)
                BigInteger numerator = (xP.pow(3).add(a.multiply(xP)).add(b)).mod(p);

                // 验证[n]P=O 若通过了所有验证,则输出“有效”
                return numerator.equals(yP.pow(2).mod(p));
        }

        /**
         * 公钥加密
         * 
         * 设需要发送的消息为比特串M,klen为M的比特长度。
         * 为了对明文M进行加密,作为加密者的用户A应实现以下运算步骤:
         * A1:用随机数发生器产生随机数k∈[1,n-1];
         * A2:计算椭圆曲线点C1=[k]G=(x1,y1),按本文本第1部分4.2.8和4.2.4给出的细节,将C1的数据类型转换为比特串;
         * A3:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出;
         * A4:计算椭圆曲线点[k]PB=(x2,y2),按本文本第1部分4.2.5和4.2.4给出的细节,将坐标x2、y2 的数据类型转换为比特串;
         * A5:计算t=KDF(x2 ∥ y2, klen),若t为全0比特串,则返回A1;
         * A6:计算C2 = M ⊕ t;
         * A7:计算C3 = Hash(x2 ∥ M ∥ y2);
         * A8:输出密文C = C1 ∥ C2 ∥ C3。
         * 
         * @param text      明文
         * @param publicKey 公钥
         * @return
         */
        public static String encryption(String text, String publicKey) {
                String texts = ArrayUtils.bytesToHex(text.getBytes(StandardCharsets.UTF_8));

                // 处理明文
                BigInteger texInteger = new BigInteger(texts, 16);
                int textLength = texts.length();
                // 处理密钥
                int length = publicKey.length();
                int lengthTwo = length >> 1;

                BigInteger[] publicKeyArr = { new BigInteger(publicKey.substring(0, lengthTwo), 16),
                                new BigInteger(publicKey.substring(lengthTwo, length), 16) };

                // A1:用随机数发生器产生随机数k∈[1,n-1];
                BigInteger randomK;
                BigInteger num;

                // 椭圆曲线点C1
                BigInteger[] C1Arr = null;
                BigInteger[] pointArr = null;

                // ECC_N 比特长度
                int ecclength = ECC_N.bitLength();

                String pointArr_0 = "";
                String pointArr_1 = "";
                do {
                        do {
                                // A1:用随机数发生器产生随机数k∈[1,n-1];
                                randomK = new BigInteger(ecclength, random);

                        } while (randomK.compareTo(BigInteger.ONE) <= 0
                                        || randomK.compareTo(ECC_N) > 0);

                        // A2:计算椭圆曲线点C1=[k]G=(x1,y1),按本文本第1部分4.2.8和4.2.4给出的细节,将C1的数据类型转换为比特串;
                        C1Arr = multiPoints(ECC_POINT, randomK);

                        // A3:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出;
                        pointArr = multiPoints(publicKeyArr, randomK);
                        pointArr_0 = pointArr[0].toString(16);
                        pointArr_1 = pointArr[1].toString(16);
                        num = new BigInteger(kdf(pointArr_0 + pointArr_1, textLength), 16);
                } while (BigInteger.ZERO.equals(num));

                // 计算C2 = M ⊕ t;
                String C2 = (texInteger.xor(num)).toString(16);
                String C3 = pointArr_0 + texInteger.toString(16) + pointArr_1;
                String C11 = C1Arr[0].toString(16);
                String C12 = C1Arr[1].toString(16);

                return textLength + "" + C11.length() + "" + C12.length() + C11 + C12 + C2 + SM3Util.encryption(C3);
        }

        /**
         * 密钥派生
         * 
         * @param bitnum
         * @param klen
         * @return
         */
        public static String kdf(String bitnum, int klen) {
                int v = 64;
                int count = (int) Math.ceil(klen / (v * 1.0));
                StringBuilder k = new StringBuilder();

                for (int i = 0; i < count; i++) {
                        StringBuilder bitString = new StringBuilder(bitnum);
                        String ks = Integer.toHexString(i + 1);

                        for (int j = 32 - ks.length(); j >= 0; j--) {
                                bitString.append("0");
                        }
                        bitString.append(ks);

                        k.append(SM3Util.encryption(bitString.toString()));

                }

                return k.substring(0, klen);
        }

        /**
         * 解密
         * 设klen为密文中C2的比特长度。
         * 为了对密文C=C1 ∥ C2 ∥ C3 进行解密,作为解密者的用户B应实现以下运算步骤:
         * B1:从C中取出比特串C1,按本文本第1部分4.2.3和4.2.9给出的细节,将C1的数据类型转换为椭
         * 圆曲线上的点,验证C1是否满足椭圆曲线方程,若不满足则报错并退出;
         * B2:计算椭圆曲线点S=[h]C1,若S是无穷远点,则报错并退出;
         * B3:计算[dB]C1=(x2,y2),按本文本第1部分4.2.5和4.2.4给出的细节,将坐标x2、y2的数据类型转
         * 换为比特串;
         * B4:计算t=KDF(x2 ∥ y2, klen),若t为全0比特串,则报错并退出;
         * B5:从C中取出比特串C2,计算M′ = C2 ⊕ t;
         * B6:计算u = Hash(x2 ∥ M′ ∥ y2),从C中取出比特串C3,若u ̸= C3,则报错并退出;
         * 5
         * B7:输出明文M′。
         * 注:解密过程的示例参见附录A
         * 
         * @param ciphertext
         * @param privateKey
         * @return
         * @throws UnsupportedEncodingException
         */
        public static String decrypt(String ciphertext, String privateKey) {

                // 获取长度数量
                Integer bitlength = Integer.parseInt(ciphertext.substring(0, 2));
                Integer C11 = Integer.parseInt(ciphertext.substring(2, 4));
                Integer C12 = Integer.parseInt(ciphertext.substring(4, 6));
                Integer C = C11 + C12;

                // 获取各个节点的数据
                ciphertext = ciphertext.substring(6);
                int length = ciphertext.length() - 64;
                String C1 = ciphertext.substring(0, C);
                String C2 = ciphertext.substring(C, length);
                String C3 = ciphertext.substring(length);

                //
                BigInteger[] C1Arr = { new BigInteger(C1.substring(0, C11), 16),
                                new BigInteger(C1.substring(C11), 16) };

                BigInteger left = C1Arr[1].modPow(BigInteger.TWO, ECC_P);

                BigInteger right = (C1Arr[0].modPow(THREE, ECC_P).add((ECC_A.multiply(C1Arr[0]))).add(ECC_B))
                                .mod(ECC_P);
                if (!left.equals(right)) {
                        // 抛异常说明 验证C1是否满足椭圆曲线方程,若不满足则报错并退出;
                        throw new IllegalArgumentException("验证C1是否满足椭圆曲线方程");
                }
                BigInteger[] C2Arr = multiPoints(C1Arr, new BigInteger(privateKey, 16));
                String C2Arr_0 = C2Arr[0].toString(16);
                String C2Arr_1 = C2Arr[1].toString(16);

                // x2y2connet=hex(x2).upper()[2:]+hex(y2).upper()[2:]

                BigInteger tInteger = new BigInteger(kdf(C2Arr_0 + C2Arr_1, bitlength), 16);
                if (tInteger.equals(BigInteger.ZERO)) {
                        // 抛异常说明 若S是无穷远点,则报错并退出;
                        throw new IllegalArgumentException("S是无穷远点");
                }

                BigInteger Mm = new BigInteger(C2, 16).xor(tInteger);
                // u=self.hex(x2)+Mm+self.hex(y2)
                if (!C3.equals(SM3Util.encryption(C2Arr_0 + Mm.toString(16) + C2Arr_1))) {
                        // 抛异常说明 若S是无穷远点,则,从C中取出比特串C3,若u ̸= C3,则报错并退出;;
                        throw new IllegalArgumentException("验证失败");
                }

                return new String(ArrayUtils.hexToByte(Mm.toString(16)), StandardCharsets.UTF_8);
        }

        /**
         * 签名
         * 
         * 设待签名的消息为M,为了获取消息M的数字签名(r,s),作为签名者的用户A应实现以下运算步
         * 骤:
         * A1:置M=ZA ∥ M;
         * A2:计算e = Hv(M),按本文本第1部分4.2.3和4.2.2给出的细节将e的数据类型转换为整数;
         * A3:用随机数发生器产生随机数k ∈[1,n-1];
         * A4:计算椭圆曲线点(x1,y1)=[k]G,按本文本第1部分4.2.7给出的细节将x1的数据类型转换为整
         * 数;
         * A5:计算r=(e+x1) modn,若r=0或r+k=n则返回A3;
         * A6:计算s = ((1 + dA)−1· (k − r · dA)) modn,若s=0则返回A3;
         * A7:按本文本第1部分4.2.1给出的细节将r、s的数据类型转换为字节串,消息M 的签名为(r,s)。
         * 
         * @param message
         * @param privateKey
         * @return
         */
        public static String sing(String message, String privateKey) {
                // 处理消息获取其的16进制骂
                String msg = ArrayUtils.bytesToHex(message.getBytes(StandardCharsets.UTF_8));
                BigInteger privateKeyInt = new BigInteger(privateKey, 16);
                // 获取公钥
                BigInteger[] multiPoint = multiPoints(ECC_POINT,
                                privateKeyInt);

                // 获取用户的其他消息ZA =H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。

                String ZA = SM3Util
                                .encryption(Integer.toHexString(msg.length()) + msg + ECC_A.toString(16)
                                                + ECC_B.toString(16)
                                                + ECC_X.toString(16) + ECC_Y.toString(16)
                                                + ArrayUtils.bytesToHex(multiPoint[0].toByteArray())
                                                + ArrayUtils.bytesToHex(multiPoint[1].toByteArray()));

                // 计算e = Hv(M) , M=ZA ∥ M;
                BigInteger e = new BigInteger(SM3Util.encryption(ZA + msg), 16);

                // 用随机数发生器产生随机数k ∈[1,n-1];
                BigInteger k;
                BigInteger[] multiPointTwo;
                BigInteger r;
                BigInteger s;

                do {
                        do {
                                do {
                                        // 生成私钥
                                        k = new BigInteger(ECC_N.bitLength(), random);
                                } while (k.compareTo(BigInteger.ONE) <= 0
                                                || k.compareTo(ECC_N) > 0);
                                // 计算椭圆曲线点(x1,y1)=[k]G,
                                multiPointTwo = multiPoints(ECC_POINT, k);

                                // 计算r=(e+x1) modn,若r=0或r+k=n则返回A3;

                                r = (e.add(multiPointTwo[0])).mod(ECC_N);
                        } while (BigInteger.ZERO.equals(k) || ECC_N.equals(r.add(k)));

                        // 计算s = ((1 + dA)−1· (k − r · dA)) modn,若s=0则返回A3;

                        s = BigInteger.ONE.add(privateKeyInt).modInverse(ECC_N)
                                        .multiply(k.subtract(r.multiply(privateKeyInt))).mod(ECC_N);
                } while (BigInteger.ZERO.equals(s));

                String rString = r.toString(16);

                return rString.length() + rString + s.toString(16);
        }

        /**
         * 验证签名
         * 
         * 为了验证收到的消息M 及其代理签名(Ga,Gab,r ,s ),验证者Garol进行以下步骤:G 1.检验r 1,n -1和s 1,n -
         * 是否成立,若不成立则验证不通过;G 2.计算e = H(M );
         * G 3.计算rab = t1 mod n;
         * G 4.计算t =(r+s) mod n;
         * G5.计算椭圆曲线上点s Gab + tra PA =(C,),计算R =(e +) modn,当R =时,则接签名,否则拒绝签名.
         * 
         * @param message
         * @param r
         * @param s
         * @param privateKey
         * @param publicKey
         * @return
         */
        public static boolean verify(String message, String sing, String publicKey) {

                String msg = ArrayUtils.bytesToHex(message.getBytes(StandardCharsets.UTF_8));
                int lengths = Integer.parseInt(sing.substring(0, 2)) + 2;
                BigInteger rInt = new BigInteger(sing.substring(2, lengths), 16);
                BigInteger sInt = new BigInteger(sing.substring(lengths), 16);

                int lengthTwo = publicKey.length() >> 1;
                BigInteger[] publicKeyArr = { new BigInteger(publicKey.substring(0, lengthTwo), 16),
                                new BigInteger(publicKey.substring(lengthTwo), 16) };

                // 获取用户的其他消息ZA =H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。
                String ZA = SM3Util.encryption(Integer.toHexString(msg.length()) + msg + ECC_A.toString(16) +
                                ECC_B.toString(16) + ECC_X.toString(16) + ECC_Y.toString(16) + publicKey);

                // 计算e = Hv(M) , M=ZA ∥ M;
                BigInteger e = new BigInteger(SM3Util.encryption(ZA + msg), 16);
                // 计算t = (r′ + s′) modn,若t = 0,则验证不通过;
                BigInteger t = (rInt.add(sInt)).mod(ECC_N);
                // 点(x′1, y′1)=[s′]G + [t]PA;

                BigInteger[] multiPointTwo1 = multiPoints(ECC_POINT, sInt);
                BigInteger[] multiPointTwo2 = multiPoints(publicKeyArr, t);

                BigInteger[] result = addPoints(multiPointTwo1, multiPointTwo2);
                BigInteger R = e.add(result[0]).mod(ECC_N);

                return R.equals(rInt);
        }

        /**
         * 两个坐标相加
         * 
         * @param P1
         * @param P2
         * @return
         */
        public static BigInteger[] addPoints(BigInteger[] P1, BigInteger[] P2) {

                if (P1[0].equals(BigInteger.ZERO) && P1[1].equals(BigInteger.ZERO)) {
                        return P2;
                }

                if (P2[0].equals(BigInteger.ZERO) && P2[1].equals(BigInteger.ZERO)) {
                        return P1;
                }

                BigInteger lambda;
                if (P1[0].equals(P2[0]) && P1[1].negate().equals(P2[2])) {
                        return new BigInteger[] { BigInteger.ZERO, BigInteger.ZERO };
                } else {
                        if (P1[0].equals(P2[0])) {
                                lambda = (P1[1].multiply(P1[1]).multiply(BigInteger.TWO)).modInverse(ECC_P)
                                                .multiply((P1[0].multiply(THREE)).add(ECC_A))
                                                .mod(ECC_P);
                        } else {
                                lambda = (P2[1].subtract(P1[1])).multiply(P2[0].subtract(P1[0]).modInverse(ECC_P))
                                                .mod(ECC_P);
                        }
                }

                BigInteger x3 = (lambda.multiply(lambda).subtract(P1[0]).subtract(P2[0])).mod(ECC_P);
                BigInteger y3 = (lambda.multiply(P1[0].subtract(x3)).subtract(P1[1])).mod(ECC_P);

                return new BigInteger[] { x3, y3 };
        }

        /**
         * B1:用随机数发生器产生随机数rB ∈ [1, n-1];
         * B2:计算椭圆曲线点RB = [rB]G=(x2,y2);
         * B3:计算x¯2 = 2w + (x2&(2w −1));
         * B4:计算tB = (dB +x¯2 ·rB)mod n;
         * B5:验证RA是否满足椭圆曲线方程,若不满足则协商失败;否则从RA中取出域元素x1,计算x¯1 = 2w + (x1&(2w −1));
         * B6:计算椭圆曲线点V = [h ·tB](PA +[x¯1]RA) = (xV ,yV ),若V是无穷远点,则B协商失败;
         * B7:计算KB=KDF(xV ∥ yV ∥ ZA ∥ ZB,klen);
         * B8:计算SB=Hash(0x02 ∥ yV ∥Hash(xV ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥ y2));
         * B9:将RB、(选项SB)发送给用户A;
         * 
         * @param dB  私钥
         * @param RA  用户A的随机参数
         * @param PA  用户A的公钥
         * @param PB  用户B的公钥
         * @param IDA A的身份标识
         * @param IDB B的身份标识
         * @return [0]:SB, [1]: RB, [2]:KB
         *         SB 选项, RB:用户B的随机数,等同于RA, KB公共密钥
         */
        public static String[] consultB1(String dB, String RA, String PA, String PB, String IDA,
                        String IDB) {
                BigInteger rB;
                do {
                        // B1:用随机数发生器产生随机数rB ∈ [1, n-1];
                        rB = new BigInteger(ECC_N.bitLength(), random);
                } while (rB.compareTo(BigInteger.ONE) <= 0
                                || rB.compareTo(ECC_N) > 0);

                // B2:计算椭圆曲线点RB = [rB]G=(x2,y2);
                BigInteger[] multiPoint = multiPoints(ECC_POINT, rB);

                // B3:计算x¯2 = 2w + (x2&(2w −1));
                // 记w=⌈(⌈log2(n)⌉/2)⌉−1。
                int w = 127;
                // 最终计算结果
                BigInteger xBar2 = countX2(multiPoint[0], w);
                // B4:计算tB = (dB +x¯2 ·rB)mod n;
                BigInteger tB = new BigInteger(dB, 16).add(xBar2.multiply(rB)).mod(ECC_N);

                BigInteger[] RAPoint = { new BigInteger(RA.substring(0, RA.length() / 2), 16),
                                new BigInteger(RA.substring(RA.length() / 2), 16) };

                // B5:验证RA是否满足椭圆曲线方程,若不满足则协商失败;否则从RA中取出域元素x1,计算x¯1 = 2w + (x1&(2w −1));
                BigInteger x1Bar2 = countX2(RAPoint[0], w);

                // 计算椭圆曲线点V = [h ·tB](PA +[x¯1]RA) = (xV ,yV ),若V是无穷远点,则B协商失败;

                // [x¯1]RA
                BigInteger[] xRA = multiPoints(RAPoint, x1Bar2);
                // (PA +[x¯1]RA
                BigInteger[] PAxRA = addPoints(RAPoint, xRA);

                // [h ·tB](PA +[x¯1]RA)
                BigInteger[] V = multiPoints(PAxRA, tB);

                // B7:计算KB=KDF(xV ∥ yV ∥ ZA ∥ ZB,klen);
                // 值ZA= H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥ yG ∥ xA ∥ yA)。
                String pString = ECC_A.toString(16) + ECC_B.toString(16) + ECC_X.toString(16) + ECC_Y.toString(16);

                String ZA = SM3Util
                                .encryption(ArrayUtils.bytesToHex(IDA.getBytes(StandardCharsets.UTF_8)) + pString
                                                + PA.substring(0, PA.length() / 2)
                                                + PA.substring(PA.length() / 2));
                // ZB= H256(ENT LB ∥ IDB ∥ a ∥ b ∥ xG ∥ yG ∥ xB ∥ yB)。
                String ZB = SM3Util
                                .encryption(ArrayUtils.bytesToHex(IDB.getBytes(StandardCharsets.UTF_8)) + pString
                                                + PB.substring(0, PB.length() / 2)
                                                + PB.substring(PB.length() / 2));

                String KBString = kdf(V[0].toString(16) + V[1].toString(16) + ZA + ZB, 32);

                // B9:将RB、(选项SB)发送给用户A;
                // 计算SB=Hash(0x02 ∥ yV ∥Hash(xV ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥ y2));
                String k = SM3Util
                                .encryption(V[0].toString(16) + ZA + ZB + RA + multiPoint[0].toString(16)
                                                + multiPoint[1].toString(16));

                return new String[] { SM3Util.encryption("02" + V[1].toString(16) + k),
                                multiPoint[0].toString(16) + multiPoint[1].toString(16), KBString };

        }

        /**
         * 计算x¯1 = 2w + (x1&(2w −1));
         * 
         * @param x
         * @param w
         * @return
         */
        public static BigInteger countX2(BigInteger x, int w) {
                BigInteger twoToThePowerOfW = BigInteger.TWO.pow(w);
                // 计算2w - 1
                BigInteger mask = twoToThePowerOfW.subtract(BigInteger.ONE);
                // 计算x2和(2w - 1)的按位与结果
                BigInteger bitwiseAndResult = x.and(mask);
                // 最终计算结果
                return twoToThePowerOfW.add(bitwiseAndResult);
        }

        /**
         * 
         * @return
         *         用户A:
         *         A4:从RA中取出域元素x1,按本文本第1部分4.2.7给出的方法将x1的数据类型转换为整数,计算x¯1 = 2 w +
         *         (x1&(2w−1));
         *         A5:计算tA = (dA +x¯1 ·rA)mod n;
         *         A6:计算x¯2 = 2w + (x2&(2w −1));
         *         A7:计算椭圆曲线点U = [h ·tA](PB + [x¯2]RB) = (xU ,yU ) ,若U是无穷远点,则A协商失败;
         *         A8:计算KA=KDF(xU ∥ yU ∥ ZA ∥ ZB,klen);
         *         A9:计算S1= Hash(0x02 ∥ yU ∥Hash(xU ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2
         *         ∥y2)),并检验S1=SB是否成立,若等式不成立则从B到A的密钥确认失败;
         *         A10:(选项)计算SA= Hash(0x03 ∥ yU ∥Hash(xU ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥
         *         y2)),并将SA发送给用户B。
         */
        public static String consultA1(String dA, String rA, String RA, String RB, String PA, String PB, String IDA,
                        String IDB,
                        String SB) {
                // A4:从RA中取出域元素x1,按本文本第1部分4.2.7给出的方法将x1的数据类型转换为整数,计算x¯1 = 2 w + (x1&(2w−1));
                int w = 127;

                BigInteger x1 = new BigInteger(RA.substring(0, RA.length() / 2), 16);
                BigInteger x1Bar2 = countX2(x1, w);

                // 计算tA = (dA +x¯1 ·rA)mod n;
                BigInteger privateKey = new BigInteger(rA, 16);
                BigInteger tA = new BigInteger(dA, 16).add(x1Bar2.multiply(privateKey)).mod(ECC_N);

                BigInteger[] RBPoint = { new BigInteger(RB.substring(0, RB.length() / 2), 16),
                                new BigInteger(RB.substring(RB.length() / 2), 16) };

                // A6:计算x¯2 = 2w + (x2&(2w −1));
                BigInteger xBar2 = countX2(RBPoint[0], w);

                // A7:计算椭圆曲线点U = [h ·tA](PB + [x¯2]RB) = (xU ,yU ) ,
                // [x¯1]RB
                BigInteger[] xRB = multiPoints(RBPoint, xBar2);

                String PBx = PB.substring(0, PB.length() / 2);
                String PBy = PB.substring(PB.length() / 2);
                // (PB +[x¯1]RB
                BigInteger[] PAxRA = addPoints(new BigInteger[] { new BigInteger(PBx, 16),
                                new BigInteger(PBy, 16) }, xRB);

                // [h ·tB](PA +[x¯1]RA)
                BigInteger[] U = multiPoints(PAxRA, tA);

                // A8:计算KA=KDF(xU ∥ yU ∥ ZA ∥ ZB,klen);
                String pString = ECC_A.toString(16) + ECC_B.toString(16) + ECC_X.toString(16) + ECC_Y.toString(16);

                String ZA = SM3Util
                                .encryption(ArrayUtils.bytesToHex(IDA.getBytes(StandardCharsets.UTF_8)) + pString
                                                + PA.substring(0, PA.length() / 2)
                                                + PA.substring(PA.length() / 2));
                // ZB= H256(ENT LB ∥ IDB ∥ a ∥ b ∥ xG ∥ yG ∥ xB ∥ yB)。

                String ZB = SM3Util.encryption(
                                ArrayUtils.bytesToHex(IDB.getBytes(StandardCharsets.UTF_8)) + pString + PBx + PBy);

                String KBString = kdf(U[0].toString(16) + U[1].toString(16) + ZA + ZB, 32);

                String k = SM3Util.encryption(U[0].toString(16) + ZA + ZB + RA + RB);

                // S1= Hash(0x02 ∥ yU ∥Hash(xU ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥y2))
                String S1 = SM3Util.encryption("02" + U[1].toString(16) + k);

                if (S1.equals(SB)) {
                        // 确认成功
                        return KBString;
                }

                return "";
        }

}

TS

/**
 * @Description: SM2加密算法工具类 算法
 *
 * @Author fengshang
 * @Date 2024-03-24 23:41
 */
export class SM2Utils {
  public static ECC_P: bigint =
    0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffffn;
  private static ECC_A: bigint =
    0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffcn;
  private static ECC_B: bigint =
    0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93n;
  private static ECC_N: bigint =
    0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123n;
  private static ECC_X: bigint =
    0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7n;
  private static ECC_Y: bigint =
    0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0n;
  private static ECC_POINT: [bigint, bigint] = [this.ECC_X, this.ECC_Y];

  private static ZERO: bigint = 0n;
  private static ONE: bigint = 1n;
  private static TWO: bigint = 2n;
  private static THREE: bigint = 3n;

  constructor() { }

  /**
   * 密钥对生成
   *
   * 密钥对的生成
   * 输入:一个有效的Fq(q = p且p为大于3的素数,或q = m)上椭圆曲线系统参数的集合。
   * 输出:与椭圆曲线系统参数相关的一个密钥对(d,P)。
   * a) 用随机数发生器产生整数d ∈ [1,n−2];
   * b) G为基点,计算点P = (xP,yP) = [d]G;(参见附录A.3.2。)
   * c) 密钥对是(d,P),其中d为私钥,P为公钥。
   * @returns
   */
  public static createKey(): [string, string] {
    let privateKey: bigint;
    do {
      // 生成私钥
      privateKey = BigInt(
        Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1
      );
    } while (privateKey <= 1n || privateKey > this.ECC_N - 2n);
    const multiPoint: [bigint, bigint] = this.multiPoints(
      this.ECC_POINT,
      privateKey
    );

    return [
      privateKey.toString(16),
      multiPoint[0].toString(16) + multiPoint[1].toString(16),
    ];
  }

  /**
   * 点位相乘
   * @param pointOnes
   * @param pointTwo
   * @returns
   */
  private static multiPoints(
    pointOnes: [bigint, bigint],
    pointTwo: bigint
  ): [bigint, bigint] {
    let point: bigint = pointTwo;
    // 初始化结果
    const multiPoint: [bigint, bigint] = [0n, 0n];
    const pointOne: [bigint, bigint] = [pointOnes[0], pointOnes[1]];

    // 判断数据是否为0

    while (0n < point) {
      // 判断取模是否等于1
      if (point % 2n === 1n) {
        this.addPoint(multiPoint, pointOne);
      }
      const temp: bigint =
        (pointOne[0] ** 2n * 3n + this.ECC_A) *
        this.modPow(pointOne[1] * 2n, this.ECC_P - this.TWO, this.ECC_P);

      const x: bigint = this.mod(temp ** 2n - pointOne[0] * 2n, this.ECC_P);
      pointOne[1] = this.mod(
        temp * (pointOne[0] - x) - pointOne[1],
        this.ECC_P
      );
      pointOne[0] = x;
      point = point / 2n;
    }

    return multiPoint;
  }

  /**
   * 点位相加
   * @param pointOne
   * @param pointTwo
   */
  private static addPoint(
    pointOne: [bigint, bigint],
    pointTwo: [bigint, bigint]
  ): void {
    // Assign the value of ECC_P here
    if (
      (pointTwo[0] === this.ZERO && pointTwo[1] === this.ZERO) ||
      (pointOne[0] === this.ZERO && pointOne[1] === this.ZERO)
    ) {
      pointOne[0] += pointTwo[0];
      pointOne[1] += pointTwo[1];
    } else {
      const temp1: bigint = pointOne[1] - pointTwo[1];
      const temp2: bigint = pointOne[0] - pointTwo[0];
      const temp3: bigint = this.modPow(
        temp2,
        this.ECC_P - this.TWO,
        this.ECC_P
      );
      const temp: bigint = temp1 * temp3;

      const x: bigint = this.mod(
        temp ** 2n - pointOne[0] - pointTwo[0],
        this.ECC_P
      );
      pointOne[1] = this.mod(
        temp * (pointOne[0] - x) - pointOne[1],
        this.ECC_P
      );
      pointOne[0] = x;
    }
  }

  /**
   * 幂取模运算
   * @param base
   * @param exponent
   * @param modulus
   * @returns
   */
  private static mod(base: bigint, modulus: bigint): bigint {
    return ((base % modulus) + modulus) % modulus;
  }

  /**
   * 幂取模运算
   * @param base
   * @param exponent
   * @param modulus
   * @returns
   */
  private static modPow(
    base: bigint,
    exponent: bigint,
    modulus: bigint
  ): bigint {
    let result = BigInt(1);

    base %= modulus;

    while (exponent > BigInt(0)) {
      if (exponent % 2n === BigInt(1)) {
        result = this.mod(result * base, modulus);
      }
      exponent = exponent / 2n;

      base = this.mod(base * base, modulus);
    }

    return result;
  }

  /**
   * 公钥加密
   *
   * 设需要发送的消息为比特串M,klen为M的比特长度。
   * 为了对明文M进行加密,作为加密者的用户A应实现以下运算步骤:
   * A1:用随机数发生器产生随机数k∈[1,n-1];
   * A2:计算椭圆曲线点C1=[k]G=(x1,y1),按本文本第1部分4.2.8和4.2.4给出的细节,将C1的数据类型转换为比特串;
   * A3:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出;
   * A4:计算椭圆曲线点[k]PB=(x2,y2),按本文本第1部分4.2.5和4.2.4给出的细节,将坐标x2、y2 的数据类型转换为比特串;
   * A5:计算t=KDF(x2 ∥ y2, klen),若t为全0比特串,则返回A1;
   * A6:计算C2 = M ⊕ t;
   * A7:计算C3 = Hash(x2 ∥ M ∥ y2);
   * A8:输出密文C = C1 ∥ C2 ∥ C3。
   *
   * @param text      明文
   * @param publicKey 公钥
   * @return
   */
  public static encryption(text: string, publicKey: string): string {
    // 处理明文
    const texts: string = ArrayUtils.bytesToHex(
      new Int8Array(new TextEncoder().encode(text))
    );

    const texInteger = BigInt("0x" + texts);

    const textLength = texts.length;

    // 处理密钥
    const length = publicKey.length;
    const lengthTwo = length >> 1;

    const publicKeyArr: [bigint, bigint] = [
      BigInt("0x" + publicKey.substring(0, lengthTwo)),
      BigInt("0x" + publicKey.substring(lengthTwo, length)),
    ];

    // A1:用随机数发生器产生随机数k∈[1,n-1];
    let randomK;
    let num;

    // 椭圆曲线点C1
    let C1Arr = null;
    let pointArr = null;

    // ECC_N 比特长度

    let pointArr_0 = "";
    let pointArr_1 = "";
    do {
      do {
        // A1:用随机数发生器产生随机数k∈[1,n-1];
        randomK = BigInt(
          Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1
        );
      } while (randomK <= 1n || randomK > this.ECC_N - 2n);


      // A2:计算椭圆曲线点C1=[k]G=(x1,y1),按本文本第1部分4.2.8和4.2.4给出的细节,将C1的数据类型转换为比特串;
      C1Arr = this.multiPoints(this.ECC_POINT, randomK);

      // A3:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出;
      pointArr = this.multiPoints(publicKeyArr, randomK);
      pointArr_0 = pointArr[0].toString(16);
      pointArr_1 = pointArr[1].toString(16);

      num = BigInt("0x" + this.kdf(pointArr_0 + pointArr_1, textLength));
    } while (num == 0n);

    // 计算C2 = M ⊕ t;
    const C2 = (texInteger ^ num).toString(16);
    const C3 = pointArr_0 + texInteger.toString(16) + pointArr_1;
    const C11 = C1Arr[0].toString(16);
    const C12 = C1Arr[1].toString(16);
    return (
      textLength +
      "" +
      C11.length +
      "" +
      C12.length +
      C11 +
      C12 +
      C2 +
      SM3Utils.encryption(C3)
    );
  }

  /**
   * 密钥派生
   * @param bitnum
   * @param klen
   * @returns
   */
  public static kdf(bitnum: string, klen: number): string {
    const v: number = 64;
    const count: number = Math.ceil(klen / v);
    let k: string = "";

    for (let i = 0; i < count; i++) {
      let bitString: string = bitnum;
      const ks: string = (i + 1).toString(16);

      for (let j = 32 - ks.length; j >= 0; j--) {
        bitString += "0";
      }

      bitString += ks;
      k += SM3Utils.encryption(bitString);
    }

    return k.substring(0, klen);
  }

  /**
   * 解密
   * 设klen为密文中C2的比特长度。
   * 为了对密文C=C1 ∥ C2 ∥ C3 进行解密,作为解密者的用户B应实现以下运算步骤:
   * B1:从C中取出比特串C1,按本文本第1部分4.2.3和4.2.9给出的细节,将C1的数据类型转换为椭
   * 圆曲线上的点,验证C1是否满足椭圆曲线方程,若不满足则报错并退出;
   * B2:计算椭圆曲线点S=[h]C1,若S是无穷远点,则报错并退出;
   * B3:计算[dB]C1=(x2,y2),按本文本第1部分4.2.5和4.2.4给出的细节,将坐标x2、y2的数据类型转
   * 换为比特串;
   * B4:计算t=KDF(x2 ∥ y2, klen),若t为全0比特串,则报错并退出;
   * B5:从C中取出比特串C2,计算M′ = C2 ⊕ t;
   * B6:计算u = Hash(x2 ∥ M′ ∥ y2),从C中取出比特串C3,若u ̸= C3,则报错并退出;
   * 5
   * B7:输出明文M′。
   * 注:解密过程的示例参见附录A
   *
   * @param ciphertext
   * @param privateKey
   * @return
   */
  public static decrypt(ciphertext: string, privateKey: string): string {
    // 获取长度数量
    const bitlength: number = parseInt(ciphertext.substring(0, 2), 10);
    const C11: number = parseInt(ciphertext.substring(2, 4), 10);
    const C12: number = parseInt(ciphertext.substring(4, 6), 10);
    const C: number = C11 + C12;

    // 获取各个节点的数据
    const tempCiphertext: string = ciphertext.substring(6);
    const length: number = tempCiphertext.length - 64;
    const C1: string = tempCiphertext.substring(0, C);
    const C2: string = tempCiphertext.substring(C, length);
    const C3: string = tempCiphertext.substring(length);

    //
    const C1Arr: [bigint, bigint] = [
      BigInt("0x" + C1.substring(0, C11)),
      BigInt("0x" + C1.substring(C11)),
    ];
    const left: bigint = this.mod(C1Arr[1] ** 2n, this.ECC_P);
    const right: bigint = this.mod(
      C1Arr[0] ** 3n + this.ECC_A * C1Arr[0] + this.ECC_B,
      this.ECC_P
    );
    if (left !== right) {
      // 抛异常说明 验证C1是否满足椭圆曲线方程,若不满足则报错并退出;
      throw new Error("验证C1是否满足椭圆曲线方程");
    }
    const C2Arr: bigint[] = this.multiPoints(C1Arr, BigInt("0x" + privateKey));
    const C2Arr_0: string = C2Arr[0].toString(16);
    const C2Arr_1: string = C2Arr[1].toString(16);

    const tInteger: bigint = BigInt(
      "0x" + this.kdf(C2Arr_0 + C2Arr_1, bitlength)
    );
    if (tInteger === 0n) {
      // 抛异常说明 若S是无穷远点,则报错并退出;
      throw new Error("S是无穷远点");
    }

    const Mm: bigint = BigInt("0x" + C2) ^ tInteger;
    // u=self.hex(x2)+Mm+self.hex(y2)
    if (C3 !== SM3Utils.encryption(C2Arr_0 + Mm.toString(16) + C2Arr_1)) {
      // 抛异常说明 若S是无穷远点,则,从C中取出比特串C3,若u ̸= C3,则报错并退出;;
      throw new Error("验证失败");
    }

    return this.bigintToUtf8String(Mm);
  }

  /**
   * bigint 转 utf8 字符串
   * @param num
   * @returns
   */
  private static bigintToUtf8String(num: bigint): string {
    const bytes = [];

    while (num > 0) {
      bytes.push(Number(num % 256n));
      num = num / 256n;
    }
    const decoder = new TextDecoder("utf-8");
    return decoder.decode(new Uint8Array(bytes.reverse()));
  }

  /**
   * 签名
   *
   * 设待签名的消息为M,为了获取消息M的数字签名(r,s),作为签名者的用户A应实现以下运算步
   * 骤:
   * A1:置M=ZA ∥ M;
   * A2:计算e = Hv(M),按本文本第1部分4.2.3和4.2.2给出的细节将e的数据类型转换为整数;
   * A3:用随机数发生器产生随机数k ∈[1,n-1];
   * A4:计算椭圆曲线点(x1,y1)=[k]G,按本文本第1部分4.2.7给出的细节将x1的数据类型转换为整
   * 数;
   * A5:计算r=(e+x1) modn,若r=0或r+k=n则返回A3;
   * A6:计算s = ((1 + dA)−1· (k − r · dA)) modn,若s=0则返回A3;
   * A7:按本文本第1部分4.2.1给出的细节将r、s的数据类型转换为字节串,消息M 的签名为(r,s)。
   *
   * @param message 消息
   * @param privateKey 私钥
   * @returns 消息的数字签名
   */
  public static sign(message: string, privateKey: string): string {
    // 处理消息获取其的16进制

    const msg: string = ArrayUtils.bytesToHex(
      new Int8Array(new TextEncoder().encode(message))
    );
    const privateKeyInt = BigInt("0x" + privateKey);
    // 获取公钥
    const multiPoint = this.multiPoints(this.ECC_POINT, privateKeyInt);

    // 获取用户的其他消息ZA =H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。
    const ZA = SM3Utils.encryption(
      msg.length.toString(16) +
      msg +
      this.ECC_A.toString(16) +
      this.ECC_B.toString(16) +
      this.ECC_X.toString(16) +
      this.ECC_Y.toString(16) +
      multiPoint[0].toString(16) +
      multiPoint[1].toString(16)
    );

    // 计算e = Hv(M) , M=ZA ∥ M;

    const e = BigInt("0x" + SM3Utils.encryption(ZA + msg));

    // 用随机数发生器产生随机数k ∈[1,n-1];
    let k;
    let multiPointTwo;
    let r;
    let s;

    do {
      do {
        do {
          // 生成私钥

          k = BigInt(Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1);
        } while (k <= 1n || k >= this.ECC_N);
        // 计算椭圆曲线点(x1,y1)=[k]G,
        multiPointTwo = this.multiPoints(this.ECC_POINT, k);

        // 计算r=(e+x1) modn,若r=0或r+k=n则返回A3;

        r = this.mod(e + multiPointTwo[0], this.ECC_N);
      } while (r === 0n || r + k === this.ECC_N);

      // 计算s = ((1 + dA)−1· (k − r · dA)) modn,若s=0则返回A3;
      s = this.mod(
        this.modInverse(1n + privateKeyInt, this.ECC_N) *
        (k - r * privateKeyInt),
        this.ECC_N
      );


    } while (s === 0n);

    const rString = r.toString(16);
    return rString.length.toString() + rString + s.toString(16);
  }

  /**
   * 求模反元素
   * @param a 要求模反元素的数
   * @param m 模数
   * @returns a 在模 m 意义下的乘法逆元
   */
  public static modInverse(a: bigint, m: bigint): bigint {
    const m0 = m;
    let y = 0n;
    let x = 1n;

    if (m === 1n) return 0n;

    while (a > 1n) {
      const q = a / m;
      let t = m;

      m = a % m;
      a = t;
      t = y;

      y = x - q * y;
      x = t;
    }

    if (x < 0n) x += m0;

    return x;
  }

  /**
   * 验证签名
   *
   * 为了验证收到的消息M 及其代理签名(Ga,Gab,r ,s ),验证者Garol进行以下步骤:G 1.检验r 1,n -1和s 1,n -
   * 是否成立,若不成立则验证不通过;G 2.计算e = H(M );
   * G 3.计算rab = t1 mod n;
   * G 4.计算t =(r+s) mod n;
   * G5.计算椭圆曲线上点s Gab + tra PA =(C,),计算R =(e +) modn,当R =时,则接签名,否则拒绝签名.
   *
   * @param message
   * @param r
   * @param s
   * @param privateKey
   * @param publicKey
   * @return
   */
  public static verify(
    message: string,
    sing: string,
    publicKey: string
  ): boolean {
    const msg: string = ArrayUtils.bytesToHex(
      new Int8Array(new TextEncoder().encode(message))
    );
    const lengths: number = parseInt(sing.substring(0, 2)) + 2;
    const rInt: bigint = BigInt("0x" + sing.substring(2, lengths));
    const sInt: bigint = BigInt("0x" + sing.substring(lengths));

    const lengthTwo = publicKey.length >> 1;
    const publicKeyArr: [bigint, bigint] = [
      BigInt("0x" + publicKey.substring(0, lengthTwo)),
      BigInt("0x" + publicKey.substring(lengthTwo)),
    ];

    const ZA: string = SM3Utils.encryption(
      msg.length.toString(16) +
      msg +
      this.ECC_A.toString(16) +
      this.ECC_B.toString(16) +
      this.ECC_X.toString(16) +
      this.ECC_Y.toString(16) +
      publicKey
    );

    const e: bigint = BigInt("0x" + SM3Utils.encryption(ZA + msg));
    const t: bigint = this.mod(rInt + sInt, this.ECC_N);

    const multiPointTwo1: [bigint, bigint] = this.multiPoints(
      this.ECC_POINT,
      sInt
    );
    const multiPointTwo2: [bigint, bigint] = this.multiPoints(publicKeyArr, t);

    const result: [bigint, bigint] = this.addPoints(multiPointTwo1, multiPointTwo2);
    const R: bigint = this.mod(e + result[0], this.ECC_N);

    return R == rInt;
  }


  /**
        * 计算x¯1 = 2w + (x1&(2w −1));
        * 
        * @param x
        * @param w
        * @return
        */
  public static countX2(x: bigint, w: number): bigint {
    const twoToThePowerOfW: bigint = BigInt(2) ** BigInt(w);
    // 计算2w - 1
    const mask: bigint = twoToThePowerOfW - BigInt(1);
    // 计算x2和(2w - 1)的按位与结果
    const bitwiseAndResult: bigint = x & mask;
    // 最终计算结果
    return twoToThePowerOfW + bitwiseAndResult;
  }


  /**
        * 两个坐标相加
        * 
        * @param P1
        * @param P2
        * @return
        */
  public static addPoints(P1: [bigint, bigint], P2: [bigint, bigint]): [bigint, bigint] {

    if (P1[0] == this.ZERO && P1[1] == this.ZERO) {
      return P2;
    }

    if (P2[0] === this.ZERO && P2[1] == this.ZERO) {
      return P1;
    }

    let lambda: bigint;
    if (P1[0] == P2[0] && P1[1] == -P2[1]) {

      return [BigInt(0), BigInt(0)];
    } else {
      if (P1[0] == P2[0]) {
        lambda = this.mod(this.modInverse(P1[1] * P1[1] * this.TWO, this.ECC_P) * (P1[0] * this.THREE + this.ECC_A), this.ECC_P);


      } else {

        const temp1: bigint = P1[1] - P2[1];
        const temp2: bigint = P1[0] - P2[0];
        const temp3: bigint = this.modPow(
          temp2,
          this.ECC_P - this.TWO,
          this.ECC_P
        );
        lambda = temp1 * temp3;

      }


    }

    const x3: bigint = this.mod(lambda * lambda - P1[0] - P2[0], this.ECC_P);
    const y3: bigint = this.mod(lambda * (P1[0] - x3) - P1[1], this.ECC_P);




    return [x3, y3];
  }


  /**
    * B1:用随机数发生器产生随机数rB ∈ [1, n-1];
    * B2:计算椭圆曲线点RB = [rB]G=(x2,y2);
    * B3:计算x¯2 = 2w + (x2&(2w −1));
    * B4:计算tB = (dB +x¯2 ·rB)mod n;
    * B5:验证RA是否满足椭圆曲线方程,若不满足则协商失败;否则从RA中取出域元素x1,计算x¯1 = 2w + (x1&(2w −1));
    * B6:计算椭圆曲线点V = [h ·tB](PA +[x¯1]RA) = (xV ,yV ),若V是无穷远点,则B协商失败;
    * B7:计算KB=KDF(xV ∥ yV ∥ ZA ∥ ZB,klen);
    * B8:计算SB=Hash(0x02 ∥ yV ∥Hash(xV ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥ y2));
    * B9:将RB、(选项SB)发送给用户A;
    * 
    * @param dB  私钥
    * @param RA  用户A的随机参数
    * @param PA  用户A的公钥
    * @param PB  用户B的公钥
    * @param IDA A的身份标识
    * @param IDB B的身份标识
    * @return [0]:SB, [1]: RB, [2]:KB
    *         SB 选项, RB:用户B的随机数,等同于RA, KB公共密钥
    */
  public static consultB1(dB: string, RA: string, PA: string, PB: string, IDA: string,
    IDB: string): string[] {
    let rB: bigint;
    do {
      // 生成私钥
      rB = BigInt(
        Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1
      );

    } while (rB <= 1n || rB > this.ECC_N - 2n);


    // B2:计算椭圆曲线点RB = [rB]G=(x2,y2);
    const multiPoint: [bigint, bigint] = this.multiPoints(this.ECC_POINT, rB);

    // B3:计算x¯2 = 2w + (x2&(2w −1));
    // 记w=⌈(⌈log2(n)⌉/2)⌉−1。
    const w: number = 127;
    // 最终计算结果
    const xBar2: bigint = this.countX2(multiPoint[0], w);
    // B4:计算tB = (dB +x¯2 ·rB)mod n;
    const tB: bigint = this.mod(BigInt("0x" + dB) + xBar2 * rB, this.ECC_N);


    const RAPoint: [bigint, bigint] = [
      BigInt("0x" + RA.slice(0, RA.length / 2)),
      BigInt("0x" + RA.slice(RA.length / 2, RA.length))
    ];

    // B5:验证RA是否满足椭圆曲线方程,若不满足则协商失败;否则从RA中取出域元素x1,计算x¯1 = 2w + (x1&(2w −1));
    const x1Bar2: bigint = this.countX2(RAPoint[0], w);

    // 计算椭圆曲线点V = [h ·tB](PA +[x¯1]RA) = (xV ,yV ),若V是无穷远点,则B协商失败;

    // [x¯1]RA
    const xRA: [bigint, bigint] = this.multiPoints(RAPoint, x1Bar2);
    // (PA +[x¯1]RA
    const PAxRA: [bigint, bigint] = this.addPoints(RAPoint, xRA);

    // [h ·tB](PA +[x¯1]RA)
    const V: [bigint, bigint] = this.multiPoints(PAxRA, tB);

    // B7:计算KB=KDF(xV ∥ yV ∥ ZA ∥ ZB,klen);
    // 值ZA= H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥ yG ∥ xA ∥ yA)。
    const pString: string = this.ECC_A.toString(16) + this.ECC_B.toString(16) + this.ECC_X.toString(16) + this.ECC_Y.toString(16);

    const ZA = SM3Utils
      .encryption(ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDA))) + pString
        + PA.slice(0, PA.length / 2)
        + PA.slice(PA.length / 2, PA.length));
    // ZB= H256(ENT LB ∥ IDB ∥ a ∥ b ∥ xG ∥ yG ∥ xB ∥ yB)。
    const ZB = SM3Utils
      .encryption(ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDB))) + pString
        + PB.slice(0, PB.length / 2)
        + PB.slice(PB.length / 2, PB.length));


    const KBString = this.kdf(V[0].toString(16) + V[1].toString(16) + ZA + ZB, 32);

    // B9:将RB、(选项SB)发送给用户A;
    // 计算SB=Hash(0x02 ∥ yV ∥Hash(xV ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥ y2));
    const k = SM3Utils
      .encryption(V[0].toString(16) + ZA + ZB + RA + multiPoint[0].toString(16)
        + multiPoint[1].toString(16));

    return [
      SM3Utils.encryption("02" + V[1].toString(16) + k),
      multiPoint[0].toString(16) + multiPoint[1].toString(16), KBString
    ];

  }



  /**
         * 
         * @return
         *         用户A:
         *         A4:从RA中取出域元素x1,按本文本第1部分4.2.7给出的方法将x1的数据类型转换为整数,计算x¯1 = 2 w +
         *         (x1&(2w−1));
         *         A5:计算tA = (dA +x¯1 ·rA)mod n;
         *         A6:计算x¯2 = 2w + (x2&(2w −1));
         *         A7:计算椭圆曲线点U = [h ·tA](PB + [x¯2]RB) = (xU ,yU ) ,若U是无穷远点,则A协商失败;
         *         A8:计算KA=KDF(xU ∥ yU ∥ ZA ∥ ZB,klen);
         *         A9:计算S1= Hash(0x02 ∥ yU ∥Hash(xU ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2
         *         ∥y2)),并检验S1=SB是否成立,若等式不成立则从B到A的密钥确认失败;
         *         A10:(选项)计算SA= Hash(0x03 ∥ yU ∥Hash(xU ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥
         *         y2)),并将SA发送给用户B。
         */
  public static consultA1(dA: string, rA: string, RA: string, RB: string, PA: string, PB: string, IDA: string,
    IDB: string,
    SB: string): string {
    // A4:从RA中取出域元素x1,按本文本第1部分4.2.7给出的方法将x1的数据类型转换为整数,计算x¯1 = 2 w + (x1&(2w−1));
    const w: number = 127;

    const x1: bigint = BigInt("0x" + RA.substring(0, RA.length / 2));
    const x1Bar2: bigint = this.countX2(x1, w);

    // 计算tA = (dA +x¯1 ·rA)mod n;
    const privateKey: bigint = BigInt("0x" + rA);
    const tA: bigint = this.mod(BigInt("0x" + dA) + (x1Bar2 * privateKey), this.ECC_N);

    const RBPoint: [bigint, bigint] = [
      BigInt("0x" + RB.substring(0, RB.length / 2)),
      BigInt("0x" + RB.substring(RB.length / 2, RB.length))
    ];

    // A6:计算x¯2 = 2w + (x2&(2w −1));
    const xBar2: bigint = this.countX2(RBPoint[0], w);

    // A7:计算椭圆曲线点U = [h ·tA](PB + [x¯2]RB) = (xU ,yU ) ,
    // [x¯1]RB
    const xRB: [bigint, bigint] = this.multiPoints(RBPoint, xBar2);

    const PBx = PB.substring(0, PB.length / 2);
    const PBy = PB.substring(PB.length / 2);
    // (PB +[x¯1]RB
    const PAxRA: [bigint, bigint] = this.addPoints([
      BigInt("0x" + PBx),
      BigInt("0x" + PBy)
    ], xRB);

    // [h ·tB](PA +[x¯1]RA)
    const U: [bigint, bigint] = this.multiPoints(PAxRA, tA);

    // A8:计算KA=KDF(xU ∥ yU ∥ ZA ∥ ZB,klen);
    const pString = this.ECC_A.toString(16) + this.ECC_B.toString(16) + this.ECC_X.toString(16) + this.ECC_Y.toString(16);

    const ZA = SM3Utils
      .encryption(ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDA))) + pString
        + PA.substring(0, PA.length / 2)
        + PA.substring(PA.length / 2, PA.length));
    // ZB= H256(ENT LB ∥ IDB ∥ a ∥ b ∥ xG ∥ yG ∥ xB ∥ yB)。

    const ZB = SM3Utils.encryption(
      ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDB))) + pString + PBx + PBy);

    const KBString = this.kdf(U[0].toString(16) + U[1].toString(16) + ZA + ZB, 32);

    const k = SM3Utils.encryption(U[0].toString(16) + ZA + ZB + RA + RB);

    // S1= Hash(0x02 ∥ yU ∥Hash(xU ∥ ZA ∥ ZB ∥ x1 ∥ y1 ∥ x2 ∥y2))
    const S1 = SM3Utils.encryption("02" + U[1].toString(16) + k);

    if (S1 == SB) {
      // 确认成功
      return KBString;
    }

    return "";
  }



}

以下是一个简单的Java代码示例,用于生成SM2公私钥对并进行加密解密操作: ```java import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.generators.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.math.ec.ECPoint; import java.math.BigInteger; import java.security.SecureRandom; public class SM2Example { public static void main(String[] args) throws InvalidCipherTextException { // 生成SM2密钥对 AsymmetricCipherKeyPair keyPair = generateSM2KeyPair(); // 获取公私钥参数 ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); // 要加密的明文 byte[] plaintext = "Hello, SM2!".getBytes(); // 使用公钥进行加密 byte[] ciphertext = encryptWithSM2(publicKey, plaintext); System.out.println("Ciphertext: " + new String(ciphertext)); // 使用私钥进行解密 byte[] decryptedText = decryptWithSM2(privateKey, ciphertext); System.out.println("Decrypted Text: " + new String(decryptedText)); // 签名和验签示例 byte[] message = "Message to be signed".getBytes(); byte[] signature = signWithSM2(privateKey, message); boolean isVerified = verifyWithSM2(publicKey, message, signature); System.out.println("Signature Verification: " + isVerified); } // 生成SM2公私钥对 public static AsymmetricCipherKeyPair generateSM2KeyPair() { AsymmetricCipherKeyPairGenerator generator = new ECKeyPairGenerator(); generator.init(new ECKeyGenerationParameters(SM2Util.getDomainParameters(), new SecureRandom())); return generator.generateKeyPair(); } // 使用公钥进行加密 public static byte[] encryptWithSM2(ECPublicKeyParameters publicKey, byte[] plaintext) throws InvalidCipherTextException { SM2Engine engine = new SM2Engine(); engine.init(true, new ParametersWithRandom(publicKey, new SecureRandom())); return engine.processBlock(plaintext, 0, plaintext.length); } // 使用私钥进行解密 public static byte[] decryptWithSM2(ECPrivateKeyParameters privateKey, byte[] ciphertext) throws InvalidCipherTextException { SM2Engine engine = new SM2Engine(); engine.init(false, privateKey); return engine.processBlock(ciphertext, 0, ciphertext.length); } // 使用私钥进行签名 public static byte[] signWithSM2(ECPrivateKeyParameters privateKey, byte[] message) { SM2Signer signer = new SM2Signer(); signer.init(true, privateKey); signer.update(message, 0, message.length); return signer.generateSignature(); } // 使用公钥进行验签 public static boolean verifyWithSM2(ECPublicKeyParameters publicKey, byte[] message, byte[] signature) { SM2Signer verifier = new SM2Signer(); verifier.init(false, publicKey); verifier.update(message, 0, message.length); return verifier.verifySignature(signature); } } ``` 在上面的示例中,我们使用了Bouncy Castle密码库(Bouncy Castle Crypto Library)来实现SM2加密算法。你需要确保已经将Bouncy Castle库添加到项目的依赖中。 此示例演示了如何生成SM2公私钥对、使用公钥进行加密、使用私钥进行解密,以及使用私钥进行签名和使用公钥进行验签。请注意,示例中的加密和解密操作是基于SM2原生的非对称加密算法,而签名和验签操作是基于SM2的数字签名算法。 你可以根据自己的需求对代码进行进一步的扩展和优化。同时,建议在实际使用时,对密钥进行适当的存储和保护,确保安全性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风殇无极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值