使用OpenSSL RSA公钥验证签名失败问题排查
问题分析与解决
你的代码核心问题是签名和验证的参数逻辑不匹配,导致验证时的哈希计算和签名时的哈希计算不一致:
错误点详解
签名阶段的错误用法
你手动计算了数据的SHA256摘要digest,然后调用RSA_sign(NID_sha256, digest, SHA256_DIGEST_LENGTH, ...)。但RSA_sign的第二个参数m的含义是:当指定哈希算法类型(如NID_sha256)时,m是原始数据,函数会自动对m计算对应哈希并完成签名。你传入的是已经计算好的摘要,导致RSA_sign会对这个摘要再计算一次SHA256哈希,最终签名的是“哈希的哈希”,而非原始数据的哈希。验证阶段的正确逻辑,但和签名不匹配
验证时你调用RSA_verify(NID_sha256, data.c_str(), data.length(), ...),这部分逻辑是对的:RSA_verify会自动对原始数据data计算SHA256哈希,然后和签名解密后的哈希对比。但因为签名的是二次哈希,自然无法匹配,导致验证失败。
修复方案
有两种修复方式,选其一即可:
方式一:让RSA_sign自动处理哈希计算(推荐)
删除手动计算SHA256的代码,直接传入原始数据给RSA_sign:
// 移除手动计算哈希的代码 // unsigned char digest[SHA256_DIGEST_LENGTH]; // SHA256(reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), digest); unsigned int signatureLength = RSA_size(encrypt_rsa_pri_key); unsigned char* signature = new unsigned char[signatureLength]; // 直接传入原始数据和长度,RSA_sign会自动计算SHA256哈希 int result = RSA_sign(NID_sha256, reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), signature, &signatureLength, encrypt_rsa_pri_key); std::string retStr(signature, signature + signatureLength); // 验证逻辑保持不变 result = RSA_verify(NID_sha256, reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), signature, signatureLength, encrypt_rsa_pub_key); // 记得释放内存 delete[] signature;
方式二:手动计算哈希,直接签名摘要(需处理ASN.1封装)
如果你必须手动计算哈希,需要将摘要封装成RSA签名要求的ASN.1结构,再进行签名:
unsigned char digest[SHA256_DIGEST_LENGTH]; SHA256(reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), digest); // 封装SHA256摘要的ASN.1结构(RSA签名验证会期望这个标准结构) unsigned char digest_with_asn1[64]; const unsigned char sha256_asn1_prefix[] = {0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; int prefix_len = sizeof(sha256_asn1_prefix); memcpy(digest_with_asn1, sha256_asn1_prefix, prefix_len); memcpy(digest_with_asn1 + prefix_len, digest, SHA256_DIGEST_LENGTH); int digest_total_len = prefix_len + SHA256_DIGEST_LENGTH; unsigned int signatureLength = RSA_size(encrypt_rsa_pri_key); unsigned char* signature = new unsigned char[signatureLength]; // 直接对封装后的摘要进行签名 int result = RSA_sign(NID_undef, digest_with_asn1, digest_total_len, signature, &signatureLength, encrypt_rsa_pri_key); std::string retStr(signature, signature + signatureLength); // 验证逻辑保持不变 result = RSA_verify(NID_sha256, reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), signature, signatureLength, encrypt_rsa_pub_key); // 记得释放内存 delete[] signature;
额外注意事项
- 必须释放
signature指向的内存,避免内存泄漏。 - 你的公钥XOR解密逻辑需确保解密后的内容是完整正确的PEM格式,虽然
BN_cmp返回0说明公钥加载正确,仍建议临时输出解密后的公钥内容,确认和注释中的PEM一致。
内容的提问来源于stack exchange,提问作者wayne
相关产品推荐
相关产品推荐

