gmkit
一套国密(SM2/SM3/SM4)Go SDK:在「密钥容器抽象」与「国密算法实现」两层间提供统一接口,支持KMS,HMS扩炸。
项目概述
gmkit 把两层接口拼成一个一致的开发面:
- 密钥容器抽象(
container/):KeyContainer 接口与三种可插拔后端(local_kv、kms、hsm),让业务代码不再耦合"密钥实际存在哪里"。
- 统一算法接口(
crypto/):CryptoProvider,当前实现 GMProvider 基于 tjfoc/gmsm 提供 SM2 签验、SM3 哈希、SM4-GCM 认证加密。
启动时一次性从容器解密并加载默认密钥到内存,运行期任何签名/加解密调用都不再触达文件 I/O 与口令派生开销。
仓库内附一组 CLI 与示例:
cmd/seed-local-kv:一键生成本地国密密钥集,写入加密 KV 文件。
cmd/dump-local-kv:查看 KV 文件中的条目,默认不打印密钥体。
example/local-kv-gm:端到端示例,串起"容器 → Provider → SM3/SM2/SM4-GCM"。
设计理念是接口先行:新增 KMS / HSM 容器后端或 legacy / hsm 算法栈,业务调用点无需改动。
Installation / 安装
# 业务侧引入库
go get github.com/det101/gmkit@latest
# CLI 工具(可独立部署,无需源码)
go install github.com/det101/gmkit/cmd/seed-local-kv@latest
go install github.com/det101/gmkit/cmd/dump-local-kv@latest
业务代码 import:
import (
"github.com/det101/gmkit/container"
"github.com/det101/gmkit/crypto"
)
仅对外发布 container/、crypto/、cmd/... 三类入口;container/internal/secretkv/ 为内部实现,外部不可直接引用。
目录结构
container/
container.go: KeyContainer 接口(读写密钥),以及 KeyLister / KeyDeleter 能力接口
types.go: 容器类型与密钥模型(含 KeyMeta)
config.go: 容器配置与校验
local_kv_container.go: 本地加密 KV 容器实现(SM4-GCM + Argon2id)
internal/secretkv/: 文件级加密读写实现,对外不可见
crypto/
provider.go: CryptoProvider 接口(哈希、签名、验签、加解密)
types.go: 算法模式与 TLS 入参模型
config.go: 算法配置与运行时组合配置
seed.go: GenerateLocalSeed 生成本地国密密钥素材(SM2/SM4/HMAC)
gm_provider.go: 基于 tjfoc/gmsm 的国密实现(SM2/SM3/SM4-GCM)
factory.go: NewProvider(ctx, cfg, container) 工厂入口
cmd/
seed-local-kv/: 命令行工具,将一份新生成的国密密钥写入加密 KV 文件
dump-local-kv/: 命令行工具,读取/列出加密 KV 文件中的条目(默认不输出密钥体)
example/
local-kv-gm/: 端到端示例,演示从 local_kv 装载到 GMProvider 完成 SM3/SM2/SM4-GCM
设计要点
- 业务侧只依赖接口,不直接依赖具体容器或算法实现。
- 容器类型支持:
local_kv:单文件 SM4-GCM 加密的本地键值存储
kms
hsm(预留)
- 算法模式支持:
KeyEntry 同时支持可导出与不可导出密钥:
- 可导出:使用
KeyBase64
- 不可导出(KMS/HSM):使用
KeyRef + Exportable=false
配置示例(接口层)
runtimeCfg := crypto.RuntimeConfig{
Container: container.ContainerConfig{
Type: container.ContainerTypeLocalKV,
LocalKV: container.LocalKVConfig{
Path: "/var/lib/gmkit/keys.enc",
Passphrase: os.Getenv("GO_GM_PASSPHRASE"),
},
},
Provider: crypto.CryptoConfig{
Mode: crypto.ModeGM,
AllowFallback: true,
},
}
if err := runtimeCfg.Container.Validate(); err != nil {
panic(err)
}
if err := runtimeCfg.Provider.Validate(); err != nil {
panic(err)
}
生成本地密钥种子
使用 cmd/seed-local-kv 一键生成 SM2 密钥对、SM4 默认主密钥、HMAC 密钥与相关标识,写入加密 KV 文件(替代历史脚本 bin/generate-kms-local-config.sh)。
方式 A:开发期 go run(需要本机有 go)
GO_GM_PASSPHRASE='your-strong-passphrase' \
go run ./cmd/seed-local-kv \
--out ./local.gmkv \
--alias local-default
方式 B:预编译二进制部署(目标机无需安装 go)
# 在带 go 的机器编译(可交叉编译)
go build -o ./bin/seed-local-kv ./cmd/seed-local-kv
# 静态二进制(推荐用于无 glibc / Alpine 等场景)
CGO_ENABLED=0 go build -ldflags='-s -w' -o ./bin/seed-local-kv ./cmd/seed-local-kv
# 把 bin/seed-local-kv 与 bin/seed-local-kv.sh 拷到目标机
GO_GM_PASSPHRASE='...' ./bin/seed-local-kv --out /var/lib/gmkit/local.gmkv
# 或继续走 shell 包装:
./bin/seed-local-kv.sh --out /var/lib/gmkit/local.gmkv
项目本身(容器/算法层)只是 Go 库;CI 编译出二进制后,运行时不再需要 go 工具链。
写入的别名(按 kms.local.* 命名沿用历史 properties 习惯):
| Alias |
类型 |
说明 |
kms.local.defaultKeyCipher.id |
string |
主密钥 keyId |
kms.local.defaultKeyCipher |
16 bytes |
SM4 默认主密钥(raw) |
kms.local.defaultAlias |
string |
默认别名 |
kms.local.defaultSm2PrivateKey |
bytes |
SM2 私钥(PKCS#8 DER) |
kms.local.defaultSm2PublicKey |
bytes |
SM2 公钥(X.509 DER) |
kms.local.defaultDataKeyId |
string |
DataKey 标识 |
kms.local.hmacKey |
32 bytes |
HMAC 密钥(raw) |
读取本地密钥种子
CLI:cmd/dump-local-kv
# 列出全部别名(默认不打印密钥体,只显示 alias / 字节数 / 更新时间)
GO_GM_PASSPHRASE='your-strong-passphrase' \
go run ./cmd/dump-local-kv --in ./local.gmkv
# 只取一个 alias,且打印 base64 密钥体
GO_GM_PASSPHRASE='...' \
go run ./cmd/dump-local-kv --in ./local.gmkv \
--alias kms.local.defaultSm2PublicKey --show-key
# JSON 格式(便于脚本/日志采集消费)
GO_GM_PASSPHRASE='...' \
go run ./cmd/dump-local-kv --in ./local.gmkv --format json --show-key
错口令会立刻失败(secretkv: incorrect passphrase),不会打印任何明文。
库集成:在你的服务里读取
package main
import (
"context"
"encoding/base64"
"log"
"os"
"github.com/det101/gmkit/container"
"github.com/det101/gmkit/crypto"
)
func main() {
c, err := container.NewLocalKVContainer(container.LocalKVConfig{
Path: "/var/lib/gmkit/local.gmkv",
Passphrase: os.Getenv("GO_GM_PASSPHRASE"),
})
if err != nil {
log.Fatalf("open kv: %v", err)
}
entry, err := c.Read(context.Background(), container.KeyQuery{
Alias: crypto.AliasDefaultSm2PrivateKey,
})
if err != nil {
log.Fatalf("read sm2 priv: %v", err)
}
privDER, _ := base64.StdEncoding.DecodeString(entry.KeyBase64)
log.Printf("loaded SM2 private key, %d bytes (PKCS#8 DER)", len(privDER))
}
国密算法实现:crypto.GMProvider
GMProvider 基于 tjfoc/gmsm 实现:构造时一次性把容器中的默认密钥(SM2 私钥/公钥、SM4 主密钥、HMAC 密钥及标识)解密、解析并缓存到内存,之后算法调用不再访问容器。
推荐用法:通过工厂
ctx := context.Background()
c, err := container.NewLocalKVContainer(container.LocalKVConfig{
Path: "/var/lib/gmkit/local.gmkv",
Passphrase: os.Getenv("GO_GM_PASSPHRASE"),
})
if err != nil { log.Fatal(err) }
provider, err := crypto.NewProvider(ctx, crypto.CryptoConfig{Mode: crypto.ModeGM}, c)
if err != nil { log.Fatal(err) }
digest, _ := provider.Hash([]byte("hello")) // SM3
sig, _ := provider.Sign([]byte("payload"), nil) // SM2 默认私钥
ok, _ := provider.Verify([]byte("payload"), sig, nil) // SM2 默认公钥
ct, _ := provider.Encrypt([]byte("plain"), nil, nil) // SM4-GCM,nonce 嵌入密文头
pt, _ := provider.Decrypt(ct, nil, nil)
_ = digest; _ = ok; _ = pt
接口语义约定
| 调用 |
nil / 空入参的回落 |
显式入参类型 |
Hash(data) |
— |
— |
Sign(data, priv) |
用容器加载的默认 SM2 私钥 |
*sm2.PrivateKey |
Verify(data, sig, pub) |
用容器加载的默认 SM2 公钥 |
*sm2.PublicKey |
Encrypt(plain, key, iv) |
key 空 → 默认 SM4 主密;iv 空 → 随机 12B nonce 并前置到密文 |
key 必须 16B;iv 必须 12B |
Decrypt(ct, key, iv) |
同上;iv 空 → 从密文头读 12B |
同上 |
调用 provider.Info() 可拿到非敏感的 KeyID/Alias/DataKeyID,可用于审计日志。
端到端示例
example/local-kv-gm/main.go 把"加载容器 → 构造 GMProvider → SM3/SM2/SM4-GCM"五步串成一个可执行示例:
# 1) 先 seed 一个本地 KV
GO_GM_PASSPHRASE='your-passphrase' go run ./cmd/seed-local-kv --out ./local.gmkv --force
# 2) 跑端到端示例
GO_GM_PASSPHRASE='your-passphrase' go run ./example/local-kv-gm --in ./local.gmkv
预期输出:
[1/5] 加载容器 : ./local.gmkv
[2/5] 初始化 GM : mode=gm keyId=local-key-... alias=local-default dataKeyId=local-datakey-...
[3/5] SM3 摘要 : 32 bytes
[4/5] SM2 签验 : sign=71 bytes, verify=true
[5/5] SM4-GCM : ct=41 bytes, decrypted="hello sm4-gcm"
当前状态
- 接口契约与配置校验已完成。
- 本地 KV 容器(
local_kv)已具备 Read/Write/Delete/List/Refresh 能力。
- 提供
cmd/seed-local-kv / cmd/dump-local-kv 用于一键初始化与查看本地国密密钥。
- 国密算法层
crypto.GMProvider 已落地(基于 tjfoc/gmsm),从容器一次性加载密钥到内存。
- KMS / HSM 容器、
legacy/hsm 算法实现仍预留,待具体接入需求时补齐。
Releasing / 发布到 GitHub
首次发布到 github.com/det101/gmkit 的标准流程:
# 1) 初始化并推送主干
git init
git add .
git commit -m "feat: initial release of gmkit (container + GMProvider)"
git branch -M main
git remote add origin git@github.com:det101/gmkit.git
git push -u origin main
# 2) 打 SemVer tag(go get 按 tag 选版本)
git tag -a v0.1.0 -m "v0.1.0: local_kv container + GMProvider (SM2/SM3/SM4-GCM)"
git push origin v0.1.0
之后业务方即可:
go get github.com/det101/gmkit@v0.1.0
后续版本沿用 SemVer:bug 修复走 vX.Y.Z+1,新功能走 vX.Y+1.0,不兼容改动走 vX+1.0.0 并把 module path 升到 github.com/det101/gmkit/v2。
License
Apache License 2.0 — see LICENSE and NOTICE for third-party attributions.