使用国密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
解密成功
解密=测试
上一篇: vue项目之服务器文件压缩
推荐阅读