小王的尴尬日常(四)--openssl 实现国密算法(签名和验签)

这篇博客详细介绍了如何使用OpenSSL库实现国密算法SM2的签名和验证过程,包括每个步骤的理论知识和操作细节,并提供了相应的代码实现。虽然优化空间有限,但对理解国密算法和OpenSSL应用具有参考价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天呢,更新了加密和解密的实现,今天我们接着来签名和验签。
还是按照王氏惯例先说一下这个理论知识:

下列符号适用于本部分。
A,B:使用公钥密码系统的两个用户。
a,b: Fq中的元素,它们定义Fq上的一条椭圆曲线E。
2dA:用户A的私钥。
E(Fq): Fq上椭圆曲线E 的所有有理点(包括无穷远点O)组成的集合。
e:密码杂凑函数作用于消息M的输出值。
e′:密码杂凑函数作用于消息M ′的输出值。
Fq
:包含q个元素的有限域。
G:椭圆曲线的一个基点,其阶为素数。
Hv
( ):消息摘要长度为v比特的密码杂凑函数。
IDA:用户A的可辨别标识。
M:待签名的消息。
M ′:待验证消息。
modn:模n运算。例如, 23mod7=2。
n:基点G的阶(n是#E(Fq)的素因子)。
O:椭圆曲线上的一个特殊点,称为无穷远点或零点,是椭圆曲线加法群的单位元。
PA:用户A的公钥。
q:有限域Fq中元素的数目。
x ∥ y: x与y的拼接,其中x、 y可以是比特串或字节串。
ZA:关于用户A的可辨别标识、部分椭圆曲线系统参数和用户A公钥的杂凑值。
(r,s):发送的签名。
(r′,s′):收到的签名。
[k]P:椭圆曲线上点P的k倍点,即, [k]P= P + P + · · · + P(k个P, k是正整数)。
[x,y]:大于或等于x且小于或等于y的整数的集合。
⌈x⌉:顶函数,大于或等于x的最小整数。例如, ⌈7⌉=7, ⌈8.3⌉=9。
⌊x⌋:底函数,小于或等于x的最大整数。例如, ⌊7⌋=7, ⌊8.3⌋=8。
#E(Fq): E(Fq)上点的数目,称为椭圆曲线E(Fq)的阶。

sm2签名算法流程

设待签名的消息为M,为了获取消息M的数字签名(r,s),作为签名者的用户A应实现以下运算步
骤:
A1:置M=ZA ∥ M;
A2:计算e = Hv(M),按本文本第1部分4.2.3和4.2.2给出的细节将e的数据类型转换为整数;
A3:用随机数发生器产生随机数k ∈[1,n-1];
A4:计算椭圆曲线点(x1,y1)=[k]G,按本文本第1部分4.2.7给出的细节将x1的数据类型转换为整
数;
A5:计算r=(e+x1) modn,若r=0或r+k=n则返回A3;
A6:计算s = ((1 + dA)−1 · (k − r · dA)) modn,若s=0则返回A3;
A7:按本文本第1部分4.2.1给出的细节将r、 s的数据类型转换为字节串,消息M 的签名为(r,s)。

下面是流程图:
签名算法流程图

sm2算法验签流程

为了检验收到的消息M ′及其数字签名(r′, s′),作为验证者的用户B应实现以下运算步骤:
B1:检验r′ ∈[1,n-1]是否成立,若不成立则验证不通过;
B2:检验s′ ∈[1,n-1]是否成立,若不成立则验证不通过;
B3:置M ′=ZA ∥ M ′;
B4:计算e′ = Hv(M ′),按本文本第1部分4.2.3和4.2.2给出的细节将e′的数据类型转换为整数;
B5:按本文本第1部分4.2.2给出的细节将r′、 s′的数据类型转换为整数,计算t = (r′ + s′) modn,
若t = 0,则验证不通过;
B6:计算椭圆曲线点(x′ 1; y1 ′ )=[s′]G + [t]PA;
B7:按本文本第1部分4.2.7给出的细节将x′ 1的数据类型转换为整数,计算R = (e′ + x′ 1) modn,检
验R=r′是否成立,若成立则验证通过;否则验证不通过。

下面是流程图:
验签流程图

最后是最关键的一步上代码:
签名的代码

int iret;
    unsigned char ENTLA[3] = {
  
  0};
    unsigned char ZA[33] = {
  
  0};

    unsigned char* Z = NULL, *M1 = NULL;
    int z_len;

    EC_POINT* kG = NULL;
    BIGNUM* k = NULL, *e = 
<think>嗯,用户现在想用C++OpenSSL实现多种加算法,包括AES-256-CBC、ChaCha20、RSA、ECC、SHA-256、HMAC,还有国密算法。首先,我需要确定用户的需求是什么。他们可能是在开发一个需要多种加功能的应用程序,或者是在学习OpenSSL的使用。不过用户之前已经问过AES-256-CBC的实现,现在扩展到了更多算法,包括国密算法,这可能意味着他们的项目需要符合中的加标准,或者他们所在的环境有合规性要求。 首先,我需要回顾一下之前给出的AES-256-CBC的示例,确保这次回答的结构类似,保持一致性,这样用户更容易理解。接下来,对于每个算法,我需要整理出对应的OpenSSL函数步骤。比如,ChaCha20在OpenSSL中的实现可能与AES类似,使用EVP接口,而RSAECC则涉及非对称加,需要使用不同的函数,比如EVP_PKEY相关的函数。 国密算法(如SM2、SM3、SM4)可能需要确认OpenSSL的版本是否支持。因为OpenSSL 1.1.1之后开始支持国密算法,但可能需要特定的编译选项或者使用分支版本。用户可能需要额外安装支持国密OpenSSL版本,比如使用内的分支或者通过配置选项启用。这部分需要明确告诉用户,否则他们可能会遇到编译错误。 然后,我需要考虑每个算法实现步骤是否足够详细,比如钥生成、加、解过程,以及必要的错误处理资源清理。例如,RSA加需要生成钥对,而ECC可能涉及椭圆曲线的选择。同时,哈希算法如SHA-256HMAC相对简单,但需要正确使用EVP摘要接口。 用户可能没有提到的细节包括钥管理、随机数生成、填充方式等,这些在加实现中非常重要。比如,IV的生成应该使用安全的随机数,而不是硬编码。此外,国密算法的示例代码可能需要更详细的说明,因为用户可能对这部分不太熟悉。 另外,用户可能希望这些示例代码能够直接复制粘贴使用,因此需要确保代码的正确性完整性,包括头文件引用、编译选项(如链接-lssl -lcrypto),以及必要的资源释放,比如EVP上下文的内存管理。 最后,要提醒用户注意安全实践,比如使用适当的钥派生函数、避免硬编码钥、处理错误返回值,以及选择正确的加模式(如GCM用于认证加)。特别是国密算法的使用可能需要遵循特定的家标准合规性要求,这一点需要强调。 综上所述,我需要组织每个算法的代码示例,分步骤说明,并附上注意事项,确保用户能够理解并正确实现这些加功能,同时满足他们的实际需求,尤其是国密算法的部分可能需要更详细的指导。</think>以下是用C++OpenSSL实现多种加算法的代码示例关键要点(版本要求:OpenSSL 1.1.1+): --- ### 一、AES-256-CBC(对称加) ```cpp #include <openssl/evp.h> std::vector<unsigned char> aes_encrypt( const std::vector<unsigned char>& plaintext, const unsigned char key[32], const unsigned char iv[16]) { EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); std::vector<unsigned char> ciphertext(plaintext.size() + EVP_CIPHER_block_size(EVP_aes_256_cbc())); int len; EVP_EncryptUpdate(ctx, ciphertext.data(), &len, plaintext.data(), plaintext.size()); int ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len); ciphertext_len += len; ciphertext.resize(ciphertext_len); EVP_CIPHER_CTX_free(ctx); return ciphertext; } ``` --- ### 二、ChaCha20(流码) ```cpp std::vector<unsigned char> chacha20_encrypt( const std::vector<unsigned char>& plaintext, const unsigned char key[32], const unsigned char iv[12]) // 96-bit IV { EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_chacha20(), NULL, key, iv); std::vector<unsigned char> ciphertext(plaintext.size()); int len; EVP_EncryptUpdate(ctx, ciphertext.data(), &len, plaintext.data(), plaintext.size()); EVP_CIPHER_CTX_free(ctx); return ciphertext; } ``` --- ### 三、RSA(非对称加) ```cpp #include <openssl/rsa.h> std::vector<unsigned char> rsa_encrypt( const std::vector<unsigned char>& plaintext, RSA* public_key) { std::vector<unsigned char> ciphertext(RSA_size(public_key)); int len = RSA_public_encrypt( plaintext.size(), plaintext.data(), ciphertext.data(), public_key, RSA_PKCS1_OAEP_PADDING); ciphertext.resize(len); return ciphertext; } // 生成钥对示例 RSA* generate_rsa_keypair(int bits = 2048) { RSA* rsa = RSA_new(); BIGNUM* bn = BN_new(); BN_set_word(bn, RSA_F4); RSA_generate_key_ex(rsa, bits, bn, NULL); BN_free(bn); return rsa; } ``` --- ### 、ECC(椭圆曲线加) ```cpp #include <openssl/ec.h> std::vector<unsigned char> ecdh_shared_secret( EC_KEY* local_key, EC_KEY* peer_public_key) { std::vector<unsigned char> secret(ECDH_size(local_key)); int len = ECDH_compute_key( secret.data(), secret.size(), EC_KEY_get0_public_key(peer_public_key), local_key, NULL); secret.resize(len); return secret; } // 生成ECC钥示例 EC_KEY* generate_ecc_key(int nid = NID_secp256k1) { EC_KEY* key = EC_KEY_new_by_curve_name(nid); EC_KEY_generate_key(key); return key; } ``` --- ### 五、SHA-256(哈希) ```cpp #include <openssl/sha.h> std::vector<unsigned char> sha256_hash( const std::vector<unsigned char>& data) { std::vector<unsigned char> digest(SHA256_DIGEST_LENGTH); SHA256(data.data(), data.size(), digest.data()); return digest; } ``` --- ### 六、HMAC(消息认证码) ```cpp #include <openssl/hmac.h> std::vector<unsigned char> hmac_sha256( const std::vector<unsigned char>& key, const std::vector<unsigned char>& data) { std::vector<unsigned char> digest(EVP_MAX_MD_SIZE); unsigned int len; HMAC(EVP_sha256(), key.data(), key.size(), data.data(), data.size(), digest.data(), &len); digest.resize(len); return digest; } ``` --- ### 七、国密算法(SM2/SM3/SM4) ```cpp // SM4-CBC加(需启用国密算法支持) std::vector<unsigned char> sm4_encrypt( const std::vector<unsigned char>& plaintext, const unsigned char key[16], const unsigned char iv[16]) { EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key, iv); std::vector<unsigned char> ciphertext(plaintext.size() + 16); int len; EVP_EncryptUpdate(ctx, ciphertext.data(), &len, plaintext.data(), plaintext.size()); int ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len); ciphertext_len += len; ciphertext.resize(ciphertext_len); EVP_CIPHER_CTX_free(ctx); return ciphertext; } // SM3哈希 std::vector<unsigned char> sm3_hash( const std::vector<unsigned char>& data) { EVP_MD_CTX* ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx, EVP_sm3(), NULL); EVP_DigestUpdate(ctx, data.data(), data.size()); std::vector<unsigned char> digest(EVP_MAX_MD_SIZE); unsigned int len; EVP_DigestFinal_ex(ctx, digest.data(), &len); digest.resize(len); EVP_MD_CTX_free(ctx); return digest; } ``` --- ### 关键注意事项 1. **OpenSSL版本要求**: - 国密算法需要OpenSSL 1.1.1+并启用`enable-sm3` `enable-sm4`编译选项 - 部分系统需要单独安装国密算法支持包 2. **安全实践**: ```cpp // 安全的随机数生成 #include <openssl/rand.h> unsigned char iv[16]; RAND_bytes(iv, sizeof(iv)); // 必须检查返回值 ``` 3. **内存管理**: ```cpp // 必须手动释放非对称加钥 RSA_free(rsa_key); EC_KEY_free(ecc_key); ``` 4. **编译参数**: ```bash g++ -o program program.cpp -lssl -lcrypto ``` --- ### 算法选择建议 | 场景 | 推荐算法 | OpenSSL常量 | |-------|-----------|--------------| | 高性能对称加 | AES-256-GCM | `EVP_aes_256_gcm()` | | 移动端流加 | ChaCha20-Poly1305 | `EVP_chacha20_poly1305()` | | 国密合规要求 | SM4-CBC/SM2 | `EVP_sm4_cbc()` | | 数字签名 | ECDSA | `EC_KEY` + `ECDSA_sign()` | 建议优先使用EVP统一接口(如`EVP_EncryptInit_ex`),而非直接调用底层函数(如`AES_encrypt`),以保证算法的可替换性代码一致性。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值