概述
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4等,SM1为不公开硬件加密,SM2、SM3、SM4作为公开的商用密码算法。
SM2
一种类似于RSA的国产算法。非对称加密,基于椭圆曲线的非对称加密算法(ECC),用于签名、密钥交换和加密。相对于RSA算法,256位的SM2密码强度已经比2048位的RSA密码强度要高,但运算速度快于RSA。
SM3
一种类似于MD5的摘要算法。消息摘要,校验结果为256位,算法的安全性要高于MD5算法和SHA-1算法,尚未有公开的针对SM3算法的碰撞攻击方法。用于数字签名和验证。
SM4
一种类似于AES的国产算法。分组对称加密算法。对称加密,密钥长度和分组长度均为128位。用于数据加密。
实现
安装依赖
go get github.com/tjfoc/gmsm/sm2
go get github.com/tjfoc/gmsm/sm3
go get github.com/tjfoc/gmsm/sm4
SM2 算法
- 生成 SM2 密钥对
priv, err := sm2.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
// 提取公钥
pub := priv.PublicKey.(*sm2.PublicKey)
- 私钥 → 十六进制字符串
func PrivateKeyToHex(priv *sm2.PrivateKey) string {
// 私钥 D 值(大整数)的字节表示
dBytes := priv.D.Bytes()
// 补零到 32 字节(SM2 私钥固定长度)
padded := make([]byte, 32)
copy(padded[32-len(dBytes):], dBytes)
return hex.EncodeToString(padded)
}
- 公钥 → 十六进制字符串 (04 + X + Y)
func PublicKeyToHex(pub *sm2.PublicKey) string {
padded := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
return hex.EncodeToString(padded)
}
- 十六进制字符串 → 私钥
func HexToPrivateKey(hexStr string) (*sm2.PrivateKey, error) {
keyBytes, err := hex.DecodeString(hexStr)
if err != nil {
return nil, err
}
if len(keyBytes) != 32 {
return nil, fmt.Errorf("invalid private key length: %d", len(keyBytes))
}
// 创建私钥对象
priv := new(sm2.PrivateKey)
priv.D = new(big.Int).SetBytes(keyBytes)
priv.Curve = sm2.P256Sm2() // 使用 SM2 曲线
// 计算对应的公钥
priv.PublicKey.X, priv.PublicKey.Y = priv.Curve.ScalarBaseMult(priv.D.Bytes())
return priv, nil
}
- 十六进制字符串 → 公钥
func HexToPublicKey(hexStr string) (*sm2.PublicKey, error) {
keyBytes, err := hex.DecodeString(hexStr)
if err != nil {
return nil, err
}
if len(keyBytes) != 65 || keyBytes[0] != 0x04 {
return nil, fmt.Errorf("invalid public key format")
}
pub := new(sm2.PublicKey)
pub.Curve = sm2.P256Sm2()
pub.X = new(big.Int).SetBytes(keyBytes[1:33]) // X 坐标 (32字节)
pub.Y = new(big.Int).SetBytes(keyBytes[33:65]) // Y 坐标 (32字节)
return pub, nil
}
- 公钥加密,私钥解密,私钥签名、公钥验签
func main() {
// 生成 SM2 私钥(自动选择默认参数)
priv, err := sm2.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
pub := priv.Public().(*sm2.PublicKey)
fmt.Printf("私钥: %s\n", PrivateKeyToHex(priv))
fmt.Printf("公钥: %s\n", PublicKeyToHex(pub))
data := []byte("Hello, 国密SM2!")
// 加密
ciphertext, err := sm2.Encrypt(pub, data, nil, sm2.C1C3C2)
if err != nil {
log.Fatalln(err.Error())
}
fmt.Printf("加密数据: %x\n", ciphertext)
// 解密
plaintext, err := sm2.Decrypt(priv, ciphertext, sm2.C1C3C2)
if err != nil {
log.Fatalln(err.Error())
}
if !bytes.Equal(plaintext, plaintext) {
log.Fatal("原文不匹配")
}
fmt.Printf("解密结果:%s \n", string(plaintext))
// sm2签名
sign, err := priv.Sign(rand.Reader, data, nil)
if err != nil {
log.Fatal(err)
}
//sm2验签
isok := pub.Verify(data, sign)
fmt.Printf("Verified: %v\n", isok)
}

SM3 算法
import (
"fmt"
"github.com/tjfoc/gmsm/sm3"
)
func main() {
data := "Hello, 国密SM3!"
h := sm3.New()
h.Write([]byte(data))
sum := h.Sum(nil)
fmt.Printf("原始数据:%s\n", data)
fmt.Printf("digest value is: %x\n", sum)
}

SM4 算法
// 加密(CBC 模式)
func sm4CbcEncrypt(key, iv, plaintext []byte) ([]byte, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}
plaintext = pkcs7Padding(plaintext, block.BlockSize()) // 填充
ciphertext := make([]byte, len(plaintext))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
return ciphertext, nil
}
// 解密(CBC 模式)
func sm4CbcDecrypt(key, iv, ciphertext []byte) ([]byte, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}
plaintext := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(plaintext, ciphertext)
return pkcs7Unpadding(plaintext, block.BlockSize()), nil
}
// PKCS7 填充
func pkcs7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// PKCS7 去填充
func pkcs7Unpadding(data []byte, blockSize int) []byte {
length := len(data)
unpadding := int(data[length-1])
return data[:(length - unpadding)]
}
func main() {
key := []byte("1234567890abcdef") // 16 字节密钥
iv := []byte("abcdef1234567890") // 16 字节 IV
data := []byte("Hello, 国密SM2!")
ciphertext, _ := sm4CbcEncrypt(key, iv, data)
fmt.Printf("加密数据: %x\n", ciphertext)
plaintext, _ := sm4CbcDecrypt(key, iv, ciphertext)
fmt.Printf("解密结果:%s \n", string(plaintext))
}

评论区