多语言国密SM互通讲解java、C#、golang、NodeJS
前言
最近由于工作需要用到golang的国密算法,但是要和java交互,翻遍了几乎所有的技术博客,对于国密算法SM2、SM3、SM4,要么是单一语言之间的交互,要么说跨语言之间无法互通,要么就是深奥晦涩的原理讲解,作为一个码农,我想大多人人追求的是代码怎么写,而不是巴拉巴拉一大堆长篇大论或者千篇一律的转载代码。
好了,废话不多说,只要各位大佬使用的是标题中涉及到的开发语言,且正在愁国密的实现,那么都能在这篇文章中找到对应的思路或者代码。
正宗原创,干货满满,希望看过的大佬多多点赞评论加关注,给博主一点动力。
一、国密算法SM2、SM3、SM4
国密即国家密码局认定的国产密码算法,即商用密码,是国家密码局制定标准的一系列算法。其中包括了对称加密算法,椭圆曲线非对称加密算法,杂凑算法。具体包括SM1、SM2、SM3、SM4等,其中:
SM1——对称加密算法,加密强度为128位,采用硬件实现,未公开;
SM2——非对称加密算法,加密强度为256位,已公开;
SM3——密码杂凑算法,杂凑值长度为32字节,已公开;
SM4——对称加密算法,加密强度为128位,已公开。
国密算法原理我就不在这班门弄斧了,请自行搜索其他人的博客。
二、环境依赖
博主使用的依赖版本可能与各位实际的环境有区别,如果各位想看不同版本依赖如何实现,可以在评论区留言。以下只写了算法需要的依赖,其他常规依赖请自行添加。
1、java
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.64</version>
</dependency>
2、C#
<package id="Portable.BouncyCastle" version="1.9.0" targetFramework="net45" />
3、NodeJS
"dependencies": {
"sm-crypto": "^0.2.1"
}
4、golang
require (
github.com/tjfoc/gmsm v1.4.2-0.20220114090716-36b992c51540
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
)
三、SM2示例
1、SM2公私钥生成
(1)java
java的算法依赖包里我没有找到现成的生成密钥的方法,所以这里需要重写两个类。
Sm2Params类里用到的固定值参数,是算法约定好的参数,所以各种语言中都是以该约定好的参数生成密钥对。
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
/**
* @author 九二战歌
* Time 1970-01-01 00:00
* Description:SM2固定推荐参数
* @version 1.0
*/
public final class Sm2Params {
public static final BigInteger P = fromHex("FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF");
public static final BigInteger A = fromHex("FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC");
public static final BigInteger B = fromHex("28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93");
public static final BigInteger N = fromHex("FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123");
public static final BigInteger GX = fromHex("32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7");
public static final BigInteger GY = fromHex("BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0");
public static final BigInteger H = BigInteger.valueOf(1L);
static final byte[] P_BYTES;
static final byte[] A_BYTES;
static final byte[] B_BYTES;
static final byte[] N_BYTES;
static final byte[] GX_BYTES;
static final byte[] GY_BYTES;
public static final ECDomainParameters SM2_DOMAIN_PARAMETERS;
static ECDomainParameters createSm2EcDomainParameters() {
ECCurve curve = new ECCurve.Fp(P, A, B, N, H);
ECPoint g = curve.createPoint(GX, GY);
return new ECDomainParameters(curve, g, N);
}
public static BigInteger fromHex(String hex) {
return new BigInteger(1, Hex.decode(hex));
}
static {
P_BYTES = BigIntegers.asUnsignedByteArray(32, P);
A_BYTES = BigIntegers.asUnsignedByteArray(32, A);
B_BYTES = BigIntegers.asUnsignedByteArray(32, B);
N_BYTES = BigIntegers.asUnsignedByteArray(32, N);
GX_BYTES = BigIntegers.asUnsignedByteArray(32, GX);
GY_BYTES = BigIntegers.asUnsignedByteArray(32, GY);
SM2_DOMAIN_PARAMETERS = createSm2EcDomainParameters();
}
}
Sm2KeyPairGenerator一个密钥生成器,继承ECKeyPairGenerator生成器,并重写其中的方法。
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto