欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用国密SM2进行接口对接遇到的问题

程序员文章站 2024-03-14 14:03:52
...

需求:对方提供的是128位公钥和64位私钥,都是16进制数据,然后我这边进行加密,再把数据进行传输。
下面展示一些 我自己思考的测试类,其中生成16进制公私钥的代码是对方提供,但不能用于加解密,所以我转换成了正常形式。
SM2Utils的国密算法工具类,直接在网上找一份,能够提供加解密功能就可以,我这里就不添加了。
直接上代码:

import com.zefu.inter.util.SM2Utils;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class Test {

    private static BigInteger n = new BigInteger("FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
    private static BigInteger p = new BigInteger("FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
    private static BigInteger a = new BigInteger("FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
    private static BigInteger b = new BigInteger("28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
    private static BigInteger gx = new BigInteger("32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
    private static BigInteger gy = new BigInteger("BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);

    private static ECDomainParameters ecc_bc_spec;

    private static SecureRandom random = new SecureRandom();
    private static ECCurve.Fp curve;
    private static ECPoint G;

    static  {
        curve = new ECCurve.Fp(p, a, b);
        G = curve.createPoint(gx, gy);
        ecc_bc_spec = new ECDomainParameters(curve, G, n);
    }
    public static void main(String[] args) {

        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        try {
            keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
        BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
        String privateKeyHex = privatekey.toString(16);
        // 公钥,16进制格式,发给前端,格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
        ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
        String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
        // 16进制128位公钥加密会报错 java.lang.IllegalArgumentException: Invalid point encoding 0x-2d
        // 但有时为了数据安全,有对接时也会转换成此形式
        System.out.println("16进制私钥privateKeyHex="+privateKeyHex);
        System.out.println("16进制加密后publicKeyHex="+publicKeyHex);
        // 自己产生的话,可以考虑是否转换成正常形式传输和加密解密
        byte[] bytes = privatekey.toByteArray();
        String p = new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
        ECPoint normalize = G.multiply(privatekey).normalize();
        byte[] encoded = normalize.getEncoded(true);
        String s = new String(Base64.getEncoder().encode(encoded), StandardCharsets.UTF_8);
        System.out.println("普通形式公钥="+s);
        System.out.println("普通形式私钥="+p);
        String encrypt = SM2Utils.encrypt("测试", s);
        String decrypt = SM2Utils.decrypt(encrypt, p);
        System.out.println("普通形式加密后="+encrypt);
        System.out.println("普通形式解密后="+decrypt);

        // 对方提供的16进制128位公钥
        byte[] encode = Hex.decode("0488bd526b852922a1f73075d42f5508a6877fc0927017015eb459f0ee41a825de3fb64505aef1e5c5dc09e44fa83f274bb67d929d99b50d01d5568523ce7e8106");
        String s2 = new String(Base64.getEncoder().encode(encode), StandardCharsets.UTF_8);
        System.out.println("s2="+s2);
        String encrypt2 = SM2Utils.encrypt("测试", "BIi9UmuFKSKh9zB11C9VCKaHf8CScBcBXrRZ8O5BqCXeP7ZFBa7x5cXcCeRPqD8nS7Z9kp2ZtQ0B1VaFI85+gQY=");
        System.out.println("加密="+encrypt2);
        // 对方提供的16进制64位私钥
        BigInteger bigInteger = new BigInteger("c1baa288dfb71dbab3c6c0ad77319499d747aab79fafb3149cc5ef23711f795", 16);
        System.out.println("bigInteger="+bigInteger);
        byte[] bytes2 = bigInteger.toByteArray();
        String p2 = new String(Base64.getEncoder().encode(bytes2), StandardCharsets.UTF_8);
        String decrypt2 = SM2Utils.decrypt(encrypt2, p2);
        System.out.println("解密="+decrypt2);
    }

}

测试的结果

16进制私钥privateKeyHex=96e251b3b82b9b198294cb287f82b7664be83b2090af24a0876a2cd3fde4cc7
16进制加密后publicKeyHex=04133e0fa3e7a60f98020b03e0750fb9c8ab238cabaec43cd5d68102c15f295d61dc33fd9911f714df98160b267220b701ef1289655d054751d1262619b11ec788
普通形式公钥=AhM+D6Pnpg+YAgsD4HUPucirI4yrrsQ81daBAsFfKV1h
普通形式私钥=CW4lGzuCubGYKUyyh/grdmS+g7IJCvJKCHaizT/eTMc=
解密成功
普通形式加密后=046cd9a3f615b6ea28395b08bbc0cccfead19df5dcb21ffb0762cad5a4377521aaf9b7262cead29b1395405babc0204c62db70d056a825e146c1832da7b48dffae93837292f15f0deb5d0827e3c24b9a0abb468ed3ce40db6db932e5dbb8b5f5c5e884ebe82467
普通形式解密后=测试
s2=BIi9UmuFKSKh9zB11C9VCKaHf8CScBcBXrRZ8O5BqCXeP7ZFBa7x5cXcCeRPqD8nS7Z9kp2ZtQ0B1VaFI85+gQY=
加密=04fee43f5e4b6b7ae702b0075ff8076a6d87cb585852e75cb381f7ffd0c1211e088dd58b664ccfa12e57111b62b8b8807ac293f72ba4cc36c4810e0920c37a141f1bbecdbe26efa2253a4ee56f5b31b7d0b22404a80ed5e84198b28bda3d1b8ea0331f60c4043e
bigInteger=5476633443977505618369540050644988899101043299003294044843441042303369738133
解密成功
解密=测试