【加密算法】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*Gjacobian_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*GSM2_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]) {// 私钥转HEXsize_t out_len = 64;SM2::Bin2Hex((char*)key.private_key, 32, pri_key, out_len);// 公钥转HEXout_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应用(如数字证书、安全通信)奠定了基础。在实际应用中,需结合具体场景强化密钥生命周期管理,确保符合国密合规要求。