国密 SM2 签名在 1 万 TPS 下 CPU 打满,如何用硬件加速卡优化
解读
- 场景定位:金融、政务、运营商等国产化环境,网关/核心服务需 1 万 TPS 持续 SM2 签名,单 CPU 核 100% 占用,RT 抖动大,已触发 SLA 告警。
- 瓶颈本质:SM2 是椭圆曲线公钥算法,签名流程含大数模乘、模逆、点乘,纯软件实现每条签名约 1.2~1.5 ms(2.5 GHz 主频),1 万 TPS 需要 10 000×1.2 ms = 12 s 的 CPU 时间,远超单核 1 s 上限,多核水平扩展又受锁竞争与上下文切换放大。
- 硬件加速卡选型:国内合规场景只能采用已过国家密码管理局检测的 PCIe 密码卡(型号目录可在国家密码管理局官网查询),常见形态为 PCIe 3.0 x8、x16,单卡理论 SM2 签名性能 15k
50k ops/s,延迟 0.050.15 ms,功耗 25~45 W,支持 SR-IOV 或多队列。 - 优化目标:在 1 万 TPS 下把 CPU 占用降到 30% 以内,P99 延迟 < 20 ms,同时满足“国密合规、可运维、可灰度、可回滚”。
知识点
- SM2 签名流程:ZA‖M → e=Hash → 随机 k → (x1,y1)=k·G → r=(e+x1) mod n → s=(1+dA)⁻¹·(k-r·dA) mod n → (r,s)。
- 密码卡卸载接口:
a. 同步阻塞:如 PKCS#11 C_Sign,调用线程阻塞,适合低并发。
b. 异步批量:如 SDF(《GMT 0016-2012》)SDF_InternalSign_ECC_Async,一次下发 128256 条,中断或轮询完成。16 个 VF,每个 VF 独立队列,NUMA 绑核后可线性扩展。
c. 多队列:SR-IOV 虚拟出 8 - 性能测试指标:
TPS、Latency(P50/P99/P999)、CPU 利用率(usr/sys/steal)、QPS per Watt、密码卡队列堆积深度、PCIe 带宽占用、上下文切换、softirq、numa_miss。 - 常用工具:
perf top -p pid 看热点函数;sar -I SUM 看硬中断分布;tuned-adm 选 network-latency;taskset/numactl 绑核;echo 1 > /sys/bus/pci/devices/…/sriov_numvfs 开 VF;dmesg | grep hwrng 确认卡内随机源。 - 合规要求:
密钥必须在卡内生成且不可明文导出;随机数须通过《GMT 0005-2012》检测;FIPS 140-2/3 与国密双证;生产环境需启用卡内门限签名或密钥分片,防止单卡失效导致业务中断。
答案
-
选型与采购
根据 1 万 TPS 目标留 50% 余量,按单卡 20k ops/s 选型,需 1 张即可;若高可用部署,采用双机双卡主备,用 Keepalived+VRRP 或 Kubernetes 自定义调度器实现故障 30 s 内切换。 -
驱动与固件
安装厂商提供的 UAC(Unified Accelerator Card)驱动,版本与内核严格对应(CentOS 7.9 用 3.10.0-1160.el7,Ubuntu 20.04 用 5.4.0-100-generic)。升级固件至 2023Q4 之后版本,修复 SM2 批量签名 DMA 泄漏 bug。 -
接口改造
业务层原来调用 BouncyCastle 的 SM2Signer,改为通过 JCA Provider 桥接厂商的 PKCS#11 .so;或者使用 JNI 直接调用 SDF 接口。
关键代码片段(伪):// 初始化 hSession = PKCS11.C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, null, null); PKCS11.C_Login(hSession, CKU_USER, pin); // 异步批量签名 for (int i = 0; i < 128; i++) { mechanism = new CK_MECHANISM(CKM_SM2_SIGN, null); PKCS11.C_SignInit(hSession, mechanism, hPrivateKey); PKCS11.C_SignUpdate(hSession, hash[i], 0, hash[i].length); future[i] = executor.submit(() -> PKCS11.C_SignFinal(hSession, out[i])); }批量深度 128 可在 1 万 TPS 下把调用次数降到 78 ops,上下文切换下降 90%。
-
线程模型与绑核
采用“1 个加密线程池 + N 个 IO 线程”模型:- 加密线程池大小 = 密码卡队列深度 / 批量大小 = 1024 / 128 = 8 线程;
- 用 numactl ‑C 8-15 ‑m 0 将加密线程绑到 NUMA0 核心,与网卡 RX 队列同 NUMA,减少跨节点内存访问;
- 打开驱动 irq affinity,把卡中断绑到剩余核心,避免抢占加密线程。
-
性能验证
基准测试:
a. 单卡单队列:openssl speed -sm2 -elapsed -multi 1,记录 18k ops/s。
b. 业务压测:用 JMeter 模拟 1 万 TPS,持续 30 min,观察 CPU usr<20%、sys<10%、P99=12 ms、密码卡队列深度<20、零错误。
c. 异常注入:拔卡、降速到 PCIe 2.0、随机 reboot,验证 Keepalived 切换与零丢包。 -
灰度与回滚
在 Kubernetes 中通过 ConfigMap 控制 feature flag:hardware.sm2=on|off;回滚时只需滚动重启 Pod,切回 BouncyCastle 软件实现,5 min 内完成。 -
交付文档
输出《SM2 硬件加速性能测试报告》,含测试环境拓扑、版本矩阵、基准数据、瓶颈分析、合规证书编号、运维手册(驱动升级步骤、密码卡替换 SOP)、应急预案,评审通过后方可上线。
拓展思考
-
若未来 TPS 涨到 5 万,单卡已无法满足,如何横向扩展?
思路:- 采用多卡并联,每张卡映射为独立 Kubernetes Extended Resource,调度器按“cryptocard/sm2”资源维度分配;
- 在 Service Mesh 中做一致性哈希,按交易号分片,保证同一客户的签名请求落到同一卡,避免跨卡状态同步;
- 引入卡内密钥分片+门限签名(GMT 0016 附录 D),2-of-3 模式,即使 1 卡故障业务无感。
-
如果业务同时需要 SM2 签名与 TLS 国密握手,如何复用加速卡?
思路:- 采用国密双证书 SSL(ECC-SM2-WITH-SM4-SM3),握手阶段私钥运算占 70%,可把 TLS 层也 offload 到同一卡;
- 使用 nginx+TaSSL 或 Tengine+gmtls 模块,配置 ssl_engine cryptodev;压测时需关注并发 8k 长连接下会话复用率,确保 1-RTT 比例 > 95%。
-
如何量化 ROI?
公式:
CPU 节省核数 = (软件 CPU 占用 – 硬件 CPU 占用) / 每核算力
电费节省 = 节省核数 × 每核功耗 × PUE × 电价
若节省 8 核,每核 10 W,PUE=1.6,电价 0.6 元/度,一年电费 = 8×10×1.6×24×365×0.6/1000 ≈ 672 元;
单卡采购价 1.2 万元,则约 18 个月回本,再加上 SLA 提升带来的监管罚款避免,ROI 可接受。