目 录CONTENT

文章目录
Go

go使用国密sm2、sm3、sm4算法

过客
2026-01-27 / 0 评论 / 0 点赞 / 2 阅读 / 0 字

概述

国密即国家密码局认定的国产密码算法。主要有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))
}

0
Go
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区