国密算法概述与 Go 语言环境搭建
我一直觉得,做国产化开发,绕不开国密。我是从 2018 年开始正式接触国密算法的,当时公司的项目要过等保,要求必须用国产密码。那会儿资料少,踩坑踩得真是刻骨铭心。今天这章,我们就先把国密这摊事儿理清楚,再把 Go 的环境搭好。你想想看,地基打不牢,后面全白搭。
1.1 什么是国密算法?
国密,全称是国家商用密码算法。说白了,就是咱们国家自己制定的密码标准。为什么不用国际通用的 RSA、AES 呢?你懂的,核心系统用别人的算法,心里总不踏实。国密算法由 国家密码管理局 发布,涵盖对称加密、非对称加密、哈希算法等几大类。
目前最常用的四个国密算法,我整理了一张表,你可以快速对照一下:
| 算法 | 类型 | 密钥长度 | 主要用途 |
|---|---|---|---|
| SM2 | 椭圆曲线非对称加密 | 256 位 | 数字签名、密钥协商、加密 |
| SM3 | 密码哈希 | 256 位(摘要) | 消息完整性校验、数字签名辅助 |
| SM4 | 分组对称加密 | 128 位 | 数据加密、通信链路保护 |
| SM9 | 基于标识的非对称加密 | 256 位 | 无证书的密钥管理、身份认证 |
嗯,说到这里,我必须提一嘴国密的 标准体系。这玩意儿不是你随便写个代码就叫国密的,必须严格遵循国标(GB/T 系列)和行标。举个例子,SM2 的签名算法,它的椭圆曲线参数是固定死的,不能自己改。我在项目里就见过有人用 OpenSSL 直接生成一个 SM2 密钥,结果放到国密 POS 机上验签失败,折腾了两天发现是曲线参数没对齐。
下面这张 SVG 图,帮你梳理一下整个国密算法的知识结构:
1.2 国密算法 vs 国际算法
很多同学会问,国密和 RSA、ECC 到底有啥本质区别?我直接告诉你结论:安全性等价,但设计哲学不同。拿 SM2 来说,它基于 256 位椭圆曲线,安全性相当于 RSA 3072 位。但 SM2 的密钥生成速度比 RSA 快得多,这在物联网设备上优势明显。
这里有几个关键区别,你写 Go 代码时会直接感受到:
- 编码格式不同:国际算法多用 DER/PEM 格式,国密标准定义了自己的 ASN.1 结构,我之前就因为序列化格式不兼容,在对接某 CA 系统时调了整整两天接口。
- 曲线参数不同:SM2 使用 sm2p256v1 曲线,跟国际的 secp256r1 不是一回事。千万别混用!
- 签名算法不同:SM2 的签名是基于椭圆曲线离散对数,但它的签名结构里包含了 Z 值(用户标识的哈希),这一点跟 ECDSA 有明显差异。
一句话总结:国密不是简单的“换参数”,而是一套独立完整的密码体系。用 Go 去封装它,其实就是做一次从国家标准到编程语言的“翻译”。
1.3 Go 语言环境搭建
好了,理论说完了,咱们开始动手。我假设你用的是 macOS 或 Linux,Windows 其实也差不多。
1.3.1 安装 Go
- 去
go.dev/dl下载最新的 Go 安装包。我个人习惯一直用官方的二进制版本,不用包管理器,因为版本控制更干净。 - 安装完成后,检查版本:
go version
# 输出示例:go version go1.22.0 linux/amd64
- 设置
GOPATH和GOROOT。虽然 Go 1.16 之后 module 模式成为默认,但我建议你还是配好GOPATH,因为很多针式工具还是会用到它:
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
避坑指南:千万不要把 GOPATH 设成项目目录。我曾经吃过这个亏,导致多个项目互相污染依赖版本,排查了快一天才意识到是 $GOPATH 的问题。
1.3.2 配置 Go Module
从 Go 1.11 开始,module 就是官方推荐的依赖管理方式。我个人习惯在每个国密项目里都这样做:
mkdir gm-crypto-demo
cd gm-crypto-demo
go mod init github.com/yourname/gm-crypto-demo
注意,go mod init 后面的模块名,建议跟你实际使用的仓库路径一致。这样别人 go get 你的包时不会报路径错误。
1.3.3 验证环境:写个 SM3 示例
光说不练假把式。我们直接写一小段代码,调用 Go 标准库里的 crypto/md5 测试一下,等后面章节我们会换成真正的国密库。先确保你整个流程跑得通:
package main
import (
"crypto/md5"
"fmt"
)
func main() {
data := []byte("hello, 国密教程")
hash := md5.Sum(data)
fmt.Printf("MD5: %x\n", hash)
}
保存为 main.go,然后执行:
go run main.go
如果看到一堆 32 位的十六进制字符串,说明你的环境完美了。
重要提醒:MD5 仅仅用来测试环境,不要在生产环境中用 MD5 做国密替代!正式应用时,我们会用 github.com/tjfoc/gmsm 或者 github.com/emmansun/gmsm 作为国密实现的底层依赖。在下一章,我们就会开始真正集成 SM3 库。
1.4 第一个国密程序:用 SM3 计算哈希
环境确认无误后,我们来装一个真正的国密库。这里以 github.com/emmansun/gmsm 为例,它性能不错,而且对国标实现得很严谨。
go get github.com/emmansun/gmsm
然后写个简单的 SM3 计算程序:
package main
import (
"fmt"
"github.com/emmansun/gmsm/sm3"
)
func main() {
data := []byte("hello, 国密教程")
h := sm3.Sm3Sum(data)
fmt.Printf("SM3: %x\n", h)
fmt.Printf("长度: %d 字节\n", len(h))
}
我的经验:第一次编译可能会报 undefined: sm3.Sm3Sum,别慌,检查一下你的 Go 版本是否 ≥ 1.18。某些国密库用了泛型,老旧版本不支持。
如果输出结果是 SM3: 5cee... (64位十六进制字符串),恭喜你,你已经在 Go 环境中成功跑通了国密代码!
1.5 本章小结
今天我们做了三件事:第一,搞懂了国密算法是什么以及它的家族成员;第二,对照了国密与国际算法的关键差异;第三,搭好了 Go 语言开发环境并成功运行了第一个 SM3 样例。
你想想看,这东西其实没那么神秘。国密就是一套有明确国家标准的密码工具集,而 Go 语言是我们用来操纵这些工具的好伙伴。环境搭好了,后面的章节就是一步一步把这些工具包装成可以重用的 Go SDK。