密钥生成审计:私钥随机性检测、公钥点合法性验证、密钥对存储安全
密钥生成,是SM2整个安全体系的起点。起点歪了,后面全白搭。我见过不少项目,算法实现没问题,但密钥生成环节出了纰漏,结果整个系统形同虚设。今天咱们就掰开揉碎,把密钥生成审计的三个核心点讲透。
私钥随机性检测:别让“随机”变成“伪随机”
SM2的私钥是一个256位的随机数。听起来简单,对吧?但“随机”这两个字,恰恰是最容易出问题的地方。
我个人习惯,审计私钥随机性时,先问三个问题:
- 随机数源是什么? 是硬件真随机数发生器,还是软件伪随机数生成器?
- 种子熵够不够? 有没有混入足够多的不可预测信息?
- 有没有后处理? 原始随机比特是否经过密码学安全的去偏和提取?
我在项目中遇到过一家厂商,他们用C语言的rand()函数生成私钥。你想想看,rand()的周期才2^31,对于256位的私钥来说,这跟没锁门有什么区别?
/dev/urandom(Linux)、CryptGenRandom(Windows)等接口。
具体检测方法,我一般做这几步:
- 源码审计:检查随机数生成器的调用链,确认没有使用
rand()、random()、Math.random()等非密码学安全函数。 - 熵源检查:确认种子是否包含系统时间、进程ID、鼠标移动、网络延迟等多源信息。
- 统计测试:对生成的私钥样本做NIST SP 800-22随机性测试,至少跑1000个样本。
- 重复性检测:检查私钥库中是否存在重复值。我曾经审计过一个系统,10万把私钥里居然有3把完全一样——随机数发生器初始化时种子没变。
ent工具对私钥文件做熵值估算。正常256位私钥的熵值应该在7.9-8.0 bits/byte之间。低于7.5就要高度警惕。
公钥点合法性验证:别让无效点混进来
私钥生成后,SM2通过标量乘法计算出公钥:P = d * G。但公钥不是算出来就完事了——它必须是一个合法的椭圆曲线点。
为什么?因为如果公钥不在曲线上,或者是一个无穷远点,后续的加密、签名验证都会出问题。更严重的是,攻击者可能利用无效点发起攻击。
公钥点合法性验证,我总结为“四步检查法”:
| 检查项 | 验证内容 | 审计方法 |
|---|---|---|
| 非无穷远点 | 公钥不能是无穷远点O | 检查坐标是否为(0,0)或特殊标记 |
| 坐标范围 | x, y 必须在[1, p-1]范围内 | 比较坐标值与素数p的大小 |
| 曲线方程 | y² ≡ x³ + ax + b (mod p) | 代入SM2曲线参数验证等式 |
| 阶的验证 | n * P = O(n为基点阶) | 验证公钥的阶是否整除n |
嗯,这里要注意:很多实现只做了前两步,忽略了“阶的验证”。但SM2标准(GM/T 0003-2012)明确要求验证n * P = O。我曾在一次审计中发现,某个实现跳过了这一步,结果攻击者可以用小阶点攻击来降低安全性。
审计要点: 公钥验证必须在公钥导入、公钥计算、公钥存储三个环节都执行。不能只在生成时验证一次,后面就不管了。
代码层面,我建议这样实现公钥验证:
// SM2公钥点合法性验证(伪代码)
bool validatePublicKey(ECPoint P) {
// 1. 检查无穷远点
if (P.isInfinity()) return false;
// 2. 检查坐标范围
if (P.x <= 0 || P.x >= p) return false;
if (P.y <= 0 || P.y >= p) return false;
// 3. 检查曲线方程
BigInt left = (P.y * P.y) % p;
BigInt right = (P.x * P.x * P.x + a * P.x + b) % p;
if (left != right) return false;
// 4. 检查阶(关键步骤)
ECPoint result = scalarMultiply(n, P);
if (!result.isInfinity()) return false;
return true;
}
密钥对存储安全:存不好,等于白干
密钥生成完了,验证通过了,然后呢?存起来。但怎么存,存哪里,谁有权访问——这些才是真正的安全分水岭。
我见过最离谱的案例:某系统把SM2私钥以明文形式存在数据库的private_key字段里,连个加密都没有。审计时我直接查到了所有用户的私钥。你想想看,这跟把家门钥匙挂在门外有什么区别?
密钥对存储安全,我建议从三个维度审计:
- 存储形态:私钥必须加密存储,推荐使用硬件安全模块(HSM)或可信执行环境(TEE)。软件方案至少要用SM4对私钥进行加密保护。
- 访问控制:谁可以读取私钥?谁可以导出私钥?必须有严格的权限分级和审计日志。
- 生命周期:密钥生成、分发、使用、备份、销毁,每个阶段都要有记录。
我个人习惯,审计密钥存储时,会画一张数据流图,追踪私钥从生成到销毁的完整路径。每个环节都要问:私钥在这里是明文还是密文?谁有权限接触?有没有日志记录?
下面这张图,是我做审计时常用的密钥生成与存储安全框架:
说白了,密钥生成审计就是三个字:真、准、牢。私钥要真随机,公钥要准合法,存储要牢不可破。任何一个环节出问题,整个密码体系就形同虚设。
我曾经审计过一个金融系统,他们SM2密钥生成全部合规,但私钥存储时用了弱密码加密。结果内部员工用暴力破解拿到了管理员私钥,伪造了交易签名。你看,前面99步都走对了,最后一步没走好,全盘皆输。
审计清单速查:
- ✅ 私钥随机数源是否为CSPRNG或硬件TRNG?
- ✅ 私钥是否通过NIST随机性测试?
- ✅ 公钥是否完成四步合法性验证(含阶的验证)?
- ✅ 私钥是否加密存储?加密密钥是否独立管理?
- ✅ 密钥访问是否有权限控制和审计日志?
- ✅ 密钥生命周期是否有完整记录?
记住,密钥生成审计不是走过场。它是整个密码安全的第一道防线。这道防线守住了,后面的加密、签名、密钥交换才有意义。