国密算法概述与 Go 语言环境搭建

我一直觉得,做国产化开发,绕不开国密。我是从 2018 年开始正式接触国密算法的,当时公司的项目要过等保,要求必须用国产密码。那会儿资料少,踩坑踩得真是刻骨铭心。今天这章,我们就先把国密这摊事儿理清楚,再把 Go 的环境搭好。你想想看,地基打不牢,后面全白搭。

1.1 什么是国密算法?

国密,全称是国家商用密码算法。说白了,就是咱们国家自己制定的密码标准。为什么不用国际通用的 RSA、AES 呢?你懂的,核心系统用别人的算法,心里总不踏实。国密算法由 国家密码管理局 发布,涵盖对称加密、非对称加密、哈希算法等几大类。

目前最常用的四个国密算法,我整理了一张表,你可以快速对照一下:

算法 类型 密钥长度 主要用途
SM2 椭圆曲线非对称加密 256 位 数字签名、密钥协商、加密
SM3 密码哈希 256 位(摘要) 消息完整性校验、数字签名辅助
SM4 分组对称加密 128 位 数据加密、通信链路保护
SM9 基于标识的非对称加密 256 位 无证书的密钥管理、身份认证
我的经验:大部分场景你只需要关注 SM2、SM3、SM4。SM9 虽然很酷,但实际落地的项目相对少,很多硬件加密机甚至还没支持它。

嗯,说到这里,我必须提一嘴国密的 标准体系。这玩意儿不是你随便写个代码就叫国密的,必须严格遵循国标(GB/T 系列)和行标。举个例子,SM2 的签名算法,它的椭圆曲线参数是固定死的,不能自己改。我在项目里就见过有人用 OpenSSL 直接生成一个 SM2 密钥,结果放到国密 POS 机上验签失败,折腾了两天发现是曲线参数没对齐。

下面这张 SVG 图,帮你梳理一下整个国密算法的知识结构:

国密算法知识体系 国密算法 (GM) 非对称加密 哈希算法 对称加密 SM2 (签名/加密) SM9 (标识加密) SM3 (摘要) SM4 (分组加密) 数字信封 / 密钥交换 无证书身份认证 完整性校验 / HMAC 链路加密 / 数据库加密 技术标准 GM/T 0002-2018 (SM2) | GM/T 0004-2018 (SM3) GM/T 0002-2018 (SM4) | GM/T 0081-2020 (SM9)

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

  1. 去 go.dev/dl 下载最新的 Go 安装包。我个人习惯一直用官方的二进制版本,不用包管理器,因为版本控制更干净。
  2. 安装完成后,检查版本:
go version
# 输出示例:go version go1.22.0 linux/amd64
  1. 设置 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。