【加密算法】SM2密钥生成与转换详解:从原理到代码实现
·
一、前言
在国密算法体系中,SM2是基于椭圆曲线密码(ECC)的非对称加密算法,广泛应用于数字签名、密钥交换等领域。在前文中,我们实现了SM2的加解密、签名与验签功能,但缺少密钥生成模块。本文将深入探讨SM2密钥的生成原理,并提供完整的C++实现代码,涵盖密钥生成、公私钥转换和十六进制编码等核心功能。
二、SM2密钥生成原理
1. 私钥的本质
SM2私钥是一个随机整数 d,需满足:
其中 n 是椭圆曲线生成元 G 的阶,对应SM2标准中预定义的256位大数。
2. 公钥的生成
公钥是椭圆曲线上的一个点 Q,由私钥 d 与生成元 G 通过点乘运算得到:
Q 的坐标 (x, y) 以64字节大端序存储。
三、核心代码实现
1. 密钥对生成函数 GenerateKey
void GenerateKey(SM2_KEY& key) {
SM2_BN d;
SM2_JACOBIAN_POINT Q;
// 生成私钥d,确保范围合法
do {
if (fn_rand(d) != 1) {
throw std::runtime_error("Private key generation failed");
}
} while (bn_is_zero(d) || bn_cmp(d, SM2_N) >= 0);
// 计算公钥Q = d*G
jacobian_point_mul_generator(&Q, d);
// 转换私钥为字节
bn_to_bytes(d, key.private_key);
// 将公钥坐标转为字节存储
jacobian_point_to_bytes(&Q, (uint8_t*)&key.public_key);
}
关键点:
• 循环生成:确保私钥不为零且小于 n。
• 点乘优化:使用雅可比坐标提升计算效率。
• 内存安全:敏感数据在栈中自动清理。
2. 私钥转公钥函数 CalcPubKey
void CalcPubKey(const char pri_key[64], char pub_key[128]) {
char bin_key[32] = {0};
size_t out_len = 32;
// 十六进制转二进制
SM2::Hex2Bin(pri_key, 64, bin_key, out_len);
SM2_BN d;
bn_from_bytes(d, (uint8_t*)bin_key);
// 计算公钥Q = d*G
SM2_JACOBIAN_POINT Q;
jacobian_point_mul_generator(&Q, d);
// 转换坐标并输出十六进制
SM2_POINT point;
jacobian_point_to_bytes(&Q, (uint8_t*)&point);
out_len = 128;
SM2::Bin2Hex((char*)&point, 64, pub_key, out_len);
}
流程解析:
- 解码私钥:将64字符十六进制字符串转为32字节二进制。
- 重建公钥:通过点乘运算生成公钥点。
- 编码输出:将坐标转为128字符十六进制字符串。
3. 密钥转十六进制 Key2Hex
void Key2Hex(const SM2_KEY& key, char pri_key[64], char pub_key[128]) {
// 私钥转HEX
size_t out_len = 64;
SM2::Bin2Hex((char*)key.private_key, 32, pri_key, out_len);
// 公钥转HEX
out_len = 128;
SM2::Bin2Hex((char*)&key.public_key, 64, pub_key, out_len);
}
输出格式:
• 私钥:64字符,如 "81EB26E941BB5AF16DF116495F90695272AE2CD63D6C4AE1678418E33730E700"
• 公钥:128字符,包含 x 和 y 坐标,如 "55B40D0660A35C53..."
四、使用示例
int main()
{
SM2 sm2;
// 生成密钥
SM2_KEY key;
char pPriKey[200] = { 0 };
char pPubKey[200] = { 0 };
sm2.GenerateKey(key);
sm2.Key2Hex(key, pPriKey, pPubKey);
std::cout << "密钥生成成功:\n私钥:" << pPriKey << "\n公钥:" << pPubKey << std::endl;
memset(pPubKey, 0, sizeof(pPubKey));
sm2.CalcPubKey(pPriKey, pPubKey);
std::cout << "私钥计算公钥成功:\n私钥:" << pPriKey << "\n公钥:" << pPubKey << std::endl;
if (!sm2.InitKey(priKey, pubKey))
{
std::cout << "初始化SM2密钥失败" << std::endl;
return;
}
std::cout << "测试数据:" << strlen(pMsg) << "," << pMsg << std::endl;
}
输出示例:
密钥生成成功:
私钥:f24b1939308a8e235cdecb742a9aae924fa9983c58dbc96ae66eba37c634cb8a
公钥:81d02cff7051b8183a4a2adaddf555f10f7aad5f93eb4b5b3bb1c00247201dda2755ac0329bc8aadcab2498473538a85d9d860094e0e9b1f5b9579fc84c118a4
私钥计算公钥成功:
私钥:f24b1939308a8e235cdecb742a9aae924fa9983c58dbc96ae66eba37c634cb8a
公钥:81d02cff7051b8183a4a2adaddf555f10f7aad5f93eb4b5b3bb1c00247201dda2755ac0329bc8aadcab2498473538a85d9d860094e0e9b1f5b9579fc84c118a4
五、安全注意事项
- 私钥保护:生成后应立即加密存储,避免内存泄漏。
- 随机数质量:使用符合国密标准的密码学安全随机数生成器(如
/dev/urandom)。 - 输入校验:在
CalcPubKey中需验证私钥的合法性。
六、总结
本文完善了SM2算法的密钥管理模块,通过底层椭圆曲线运算实现了密钥生成、转换和推导功能。这些函数为构建完整的SM2应用(如数字证书、安全通信)奠定了基础。在实际应用中,需结合具体场景强化密钥生命周期管理,确保符合国密合规要求。
更多推荐

所有评论(0)