SM2
SM2 是由中国国家密码管理局制定的椭圆曲线公钥密码算法(GM/T 0003-2012),是中国商用密码标准的核心算法之一。dongle 支持标准和流式 SM2 加密,提供多种密文格式和性能优化选项。
支持以下密文格式(通过 Sm2CipherMode 指定):
- C1C3C2:国密标准推荐格式(默认),裸字节结构为
0x04 || C1(64字节) || C3(32字节) || C2(密文数据)- C1:椭圆曲线点(随机数生成)
- C3:SM3 消息摘要(用于完整性验证)
- C2:加密后的数据
- C1C2C3:旧标准兼容格式,裸字节结构为
0x04 || C1(64字节) || C2(密文数据) || C3(32字节) - ASN1C1C3C2:ASN.1 DER 格式,密文为
SEQUENCE { x INTEGER, y INTEGER, C3 OCTET STRING, C2 OCTET STRING },即 BouncyCastle、Java 与多数国密网关的默认输出,不带0x04前缀 - ASN1C1C2C3:ASN.1 DER 格式,密文为
SEQUENCE { x INTEGER, y INTEGER, C2 OCTET STRING, C3 OCTET STRING }
两种 ASN.1 格式均使用
cryptobyte解析,数据被截断或不是合法 DER 时直接返回错误,不会被静默误读。
支持以下性能优化选项:
- Window 窗口大小:控制椭圆曲线运算的预计算窗口(2-6),默认为 4
- 窗口越大,加密速度越快,但内存占用略高
- 推荐使用默认值 4 或 5 以获得最佳性能
注意事项:
- 密钥格式:使用
PKCS#8格式存储私钥,使用SPKI/PKIX格式存储公钥 - 密文格式:加密和解密必须使用相同的
Sm2CipherMode(C1C3C2、C1C2C3、ASN1C1C3C2、ASN1C1C2C3之一) - 密钥材料:
SetPublicKey/SetPrivateKey除 PEM 外,还接受 base64 或 hex 编码的 DER,以及裸密钥材料(公钥为 65 字节非压缩点,私钥为 32 字节标量),并统一归一化为 PEM - 数据安全:SM2 提供 128 位安全强度,相当于 RSA 3072 位
- 互操作性:与 OpenSSL、BouncyCastle、Java 等库互操作时,需确认对方使用的密文格式;多数国密网关输出 ASN.1 格式
- 私钥安全:私钥必须妥善保管,不能泄露
- 标准符合:完全符合 GM/T 0003.4-2012(加密算法)和 GM/T 0003.5-2012(曲线参数)
导入相关模块:
go
import (
"github.com/dromara/dongle"
"github.com/dromara/dongle/crypto/keypair"
)创建密钥对
go
kp := keypair.NewSm2KeyPair()
// 设置密文格式(可选,默认为 C1C3C2)
kp.SetMode(keypair.C1C3C2)
// 与 BouncyCastle、Java 及多数国密网关互操作时使用 ASN.1 格式
// kp.SetMode(keypair.ASN1C1C3C2)
// 设置窗口大小(可选,默认为 4,范围 2-6)
kp.SetWindow(4)生成密钥对
go
// 生成 SM2 密钥对(256 位椭圆曲线)
err := kp.GenKeyPair()
if err != nil {
panic(err)
}
// 获取 PEM 格式公钥
publicKey := kp.PublicKey
// 获取 PEM 格式私钥
privateKey := kp.PrivateKey从已有 PEM 格式密钥设置密钥对
go
// 设置 PEM 格式公钥
kp.PublicKey = []byte(`-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEv6TXzg5xkLbTkzQzmucCcGbVF1pv
BuOVxSa7tZEvT1bEKqF7WJx8UQ/a7R5YDslbMxPByZGqfPaeQEpwv3emQg==
-----END PUBLIC KEY-----`)
// 设置 PEM 格式私钥
kp.PrivateKey = []byte(`-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgsXklJOzFBfCXQGaP
5kVornBxt1WjgUIv21Imv7ALAtygCgYIKoEcz1UBgi2hRANCAAS/pNfODnGQttOT
NDOa5wJwZtUXWm8G45XFJru1kS9PVsQqoXtYnHxRD9rtHlgOyVszE8HJkap89p5A
SnC/d6ZC
-----END PRIVATE KEY-----`)从已有 DER 格式密钥设置密钥对
除 base64 编码的 DER 外,SetPublicKey / SetPrivateKey 还接受 hex 编码的 DER 以及裸密钥材料(公钥为 65 字节非压缩点,私钥为 32 字节标量),并将结果统一归一化为 PEM。
go
// 设置 Base64 编码的 DER 格式公钥
kp.SetPublicKey([]byte("MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEv6TXzg5xkLbTkzQzmucCcGbVF1pvBuOVxSa7tZEvT1bEKqF7WJx8UQ/a7R5YDslbMxPByZGqfPaeQEpwv3emQg=="))
// 设置 Base64 编码的 DER 格式私钥
kp.SetPrivateKey([]byte("MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgsXklJOzFBfCXQGaP5kVornBxt1WjgUIv21Imv7ALAtygCgYIKoEcz1UBgi2hRANCAAS/pNfODnGQttOTNDOa5wJwZtUXWm8G45XFJru1kS9PVsQqoXtYnHxRD9rtHlgOyVszE8HJkap89p5ASnC/d6ZC"))将 DER 格式密钥格式化成 PEM 格式
go
// 将 base64 编码的 DER 格式公钥格式化为 PEM 格式
publicKey, err := kp.FormatPublicKey([]byte("MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEv6TXzg5xkLbTkzQzmucCcGbVF1pvBuOVxSa7tZEvT1bEKqF7WJx8UQ/a7R5YDslbMxPByZGqfPaeQEpwv3emQg=="))
// 将 base64 编码的 DER 格式私钥格式化为 PEM 格式
privateKey, err := kp.FormatPrivateKey([]byte("MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgsXklJOzFBfCXQGaP5kVornBxt1WjgUIv21Imv7ALAtygCgYIKoEcz1UBgi2hRANCAAS/pNfODnGQttOTNDOa5wJwZtUXWm8G45XFJru1kS9PVsQqoXtYnHxRD9rtHlgOyVszE8HJkap89p5ASnC/d6ZC"))将 PEM 格式密钥压缩成 DER 格式
go
// 将 PEM 格式公钥压缩成经过 base64 编码的 DER 格式(提取 PEM 中的 base64 编码 DER)
publicKey := kp.CompressPublicKey([]byte(`-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEv6TXzg5xkLbTkzQzmucCcGbVF1pv
BuOVxSa7tZEvT1bEKqF7WJx8UQ/a7R5YDslbMxPByZGqfPaeQEpwv3emQg==
-----END PUBLIC KEY-----`))
// 将 PEM 格式私钥压缩成经过 base64 编码的 DER 格式(提取 PEM 中的 base64 编码 DER)
privateKey := kp.CompressPrivateKey([]byte(`-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgsXklJOzFBfCXQGaP
5kVornBxt1WjgUIv21Imv7ALAtygCgYIKoEcz1UBgi2hRANCAAS/pNfODnGQttOT
NDOa5wJwZtUXWm8G45XFJru1kS9PVsQqoXtYnHxRD9rtHlgOyVszE8HJkap89p5A
SnC/d6ZC
-----END PRIVATE KEY-----`))
CompressPublicKey/CompressPrivateKey通过pem.Decode解析输入,因此可以处理带有Bag Attributes等可选块头的 PEM。
公钥加密
SM2 流式加密会在内部缓存全部明文,在
Close时一次性加密输出。
输入数据
go
// 输入字符串
encrypter := dongle.Encrypt.FromString("hello world").BySm2(kp)
// 输入字节切片
encrypter := dongle.Encrypt.FromBytes([]byte("hello world")).BySm2(kp)
// 输入文件流
file, _ := os.Open("test.txt")
encrypter := dongle.Encrypt.FromFile(file).BySm2(kp)
// 检查加密错误
if encrypter.Error != nil {
fmt.Printf("加密错误: %v\n", encrypter.Error)
return
}输出数据
go
// 输出 Hex 编码字符串
hexString := encrypter.ToHexString() // 例如:047fae94fd1a8b880d8d5454dd8df30c40...
// 输出 Hex 编码字节切片
hexBytes := encrypter.ToHexBytes() // 例如:[]byte("047fae94fd1a8b880d8d5454dd8df30c40...")
// 输出 Base64 编码字符串
base64String := encrypter.ToBase64String() // 例如:BH+ulP0ai4gNjVRU3Y3zDEA=...
// 输出 Base64 编码字节切片
base64Bytes := encrypter.ToBase64Bytes() // 例如:[]byte("BH+ulP0ai4gNjVRU3Y3zDEA=...")
// 输出未编码原始字符串
rawString := encrypter.ToRawString()
// 输出未编码原始字节切片
rawBytes := encrypter.ToRawBytes()私钥解密
输入数据
go
// 输入 Hex 编码字符串
decrypter := dongle.Decrypt.FromHexString(hexString).BySm2(kp)
// 输入 Hex 编码字节切片
decrypter := dongle.Decrypt.FromHexBytes(hexBytes).BySm2(kp)
// 输入 Hex 编码文件流
file, _ := os.Open("encrypted.hex")
decrypter := dongle.Decrypt.FromHexFile(file).BySm2(kp)
// 输入 Base64 编码字符串
decrypter := dongle.Decrypt.FromBase64String(base64String).BySm2(kp)
// 输入 Base64 编码字节切片
decrypter := dongle.Decrypt.FromBase64Bytes(base64Bytes).BySm2(kp)
// 输入 Base64 编码文件流
file, _ := os.Open("encrypted.base64")
decrypter := dongle.Decrypt.FromBase64File(file).BySm2(kp)
// 输入原始字符串
decrypter := dongle.Decrypt.FromRawString(rawString).BySm2(kp)
// 输入原始字节切片
decrypter := dongle.Decrypt.FromRawBytes(rawBytes).BySm2(kp)
// 输入原始文件流
file, _ := os.Open("encrypted.bin")
decrypter := dongle.Decrypt.FromRawFile(file).BySm2(kp)
// 检查解密错误
if decrypter.Error != nil {
fmt.Printf("解密错误: %v\n", decrypter.Error)
return
}输出数据
go
// 输出解密后的字符串
decrypter.ToString() // hello world
// 输出解密后的字节切片
decrypter.ToBytes() // []byte("hello world")