EVP_DigestVerifyFinal始终返回0的OpenSSL签名验证问题求助
我来帮你排查这个签名验证始终失败的问题——看了你的代码后,发现几个关键的错误点,我们一步步来解决:
问题分析与修复方案
1. 核心问题点拆解
你的代码里有几个致命问题,导致无论签名有效与否,EVP_DigestVerifyFinal都返回0:
- EVP_MD_CTX上下文复用错误:类构造函数中仅创建一次上下文,但
EVP_DigestVerify系列方法要求每次验证都使用干净的上下文,旧状态会干扰新的验证流程。 - 证书与公钥获取无错误处理:
PEM_read_X509和X509_get_pubkey可能失败,但你没有检查返回值,导致pubkey_为空,后续验证必然失败。 - 签名读取逻辑不严谨:固定用256字节读取签名,未检查文件大小和读取完整性,可能读取不完整的签名数据。
- 返回值理解偏差:OpenSSL中
EVP_DigestVerifyFinal返回1是成功,0是签名无效,负数是函数调用错误,你的代码把非1都归为错误,混淆了“验证失败”和“程序错误”。
2. 修正后的完整代码示例
首先修复构造函数,添加资源校验与析构释放
#define DEFINE_RSA_SIGSIZE 256 #define R_ERROR_NONE 0 #define DEFINE_SHA256_LENGTH 32 class My_RsaSigVerify { private: EVP_MD_CTX* crypto_ctx_; X509* cert_; EVP_PKEY* pubkey_; public: My_RsaSigVerify(const char *certfile); ~My_RsaSigVerify(); int verify_sha256(unsigned char *hash, unsigned int hsize, const char *sigfile); }; // 构造函数:添加完整错误校验 My_RsaSigVerify::My_RsaSigVerify(const char *certfile) : crypto_ctx_(NULL), cert_(NULL), pubkey_(NULL) { FILE *fp = fopen(certfile, "r"); if (!fp) { printf("My_RsaSigVerify: Failed to open cert file: %s\n", certfile); return; } cert_ = PEM_read_X509(fp, NULL, NULL, NULL); if (!cert_) { printf("My_RsaSigVerify: Failed to read X509 certificate\n"); fclose(fp); return; } pubkey_ = X509_get_pubkey(cert_); if (!pubkey_) { printf("My_RsaSigVerify: Failed to extract public key from cert\n"); X509_free(cert_); cert_ = NULL; fclose(fp); return; } fclose(fp); } // 析构函数:释放OpenSSL资源,避免内存泄漏 My_RsaSigVerify::~My_RsaSigVerify() { if (crypto_ctx_) EVP_MD_CTX_free(crypto_ctx_); if (pubkey_) EVP_PKEY_free(pubkey_); if (cert_) X509_free(cert_); }
修复verify_sha256方法,确保每次验证用干净上下文
int My_RsaSigVerify::verify_sha256(unsigned char *hash, unsigned int hsize, const char *sigfile) { int result = R_ERROR_NONE; FILE *fp = NULL; EVP_MD_CTX *ctx = EVP_MD_CTX_create(); // 每次验证创建新上下文 if (!pubkey_ || !hash || !sigfile || !ctx) { printf("My_RsaSigVerify:verify_sha256: NULL check failed:%p:%p:%p:%p\n", pubkey_, hash, sigfile, ctx); result = -1; goto cleanup; } // 初始化验证上下文 int evp_dvi = EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey_); if (1 != evp_dvi) { printf("My_RsaSigVerify:verify_sha256: EVP_DigestVerifyInit failed, code=[%d]\n", evp_dvi); result = evp_dvi; goto cleanup; } // 读取签名文件:先获取文件大小,确保读取完整 fp = fopen(sigfile, "rb"); if (!fp) { printf("My_RsaSigVerify:verify_sha256: Failed to open signature file\n"); result = -1; goto cleanup; } fseek(fp, 0, SEEK_END); long sig_size = ftell(fp); fseek(fp, 0, SEEK_SET); if (sig_size <= 0 || sig_size > DEFINE_RSA_SIGSIZE) { printf("My_RsaSigVerify:verify_sha256: Invalid signature file size: %ld\n", sig_size); result = -1; goto cleanup; } unsigned char signature[DEFINE_RSA_SIGSIZE] = {0}; unsigned int read_size = fread(signature, 1, sig_size, fp); if (read_size != (unsigned int)sig_size) { printf("My_RsaSigVerify:verify_sha256: Incomplete signature read: %u/%ld\n", read_size, sig_size); result = -1; goto cleanup; } // 更新待验证的哈希值 int evp_dvu = EVP_DigestVerifyUpdate(ctx, hash, hsize); if (1 != evp_dvu) { printf("My_RsaSigVerify:verify_sha256: EVP_DigestVerifyUpdate failed, code=[%d]\n", evp_dvu); result = evp_dvu; goto cleanup; } // 最终验证:区分成功、签名无效、程序错误三种情况 int evp_dvf = EVP_DigestVerifyFinal(ctx, signature, (size_t)read_size); if (1 == evp_dvf) { printf("My_RsaSigVerify:verify_sha256: Signature verified successfully\n"); result = 1; } else if (0 == evp_dvf) { printf("My_RsaSigVerify:verify_sha256: Invalid signature\n"); result = 0; } else { printf("My_RsaSigVerify:verify_sha256: EVP_DigestVerifyFinal error, code=[%d]\n", evp_dvf); result = evp_dvf; } cleanup: if (fp) fclose(fp); if (ctx) EVP_MD_CTX_free(ctx); printf("My_RsaSigVerify:verify_sha256() end\n"); return result; }
修复verifySignature的判断逻辑
bool verifySignature(const char *file, const char* sigfile, const char* certfile) { if ((!file)||(!sigfile)||(!certfile)) return false; unsigned char buf[DEFINE_SHA256_LENGTH]; Hash hash; if (hash.sha(file, buf, DEFINE_SHA256_LENGTH) != 0) { printf("verifySignature: SHA256 hash calculation failed\n"); return false; } My_RsaSigVerify rsa(certfile); int verify_ret = rsa.verify_sha256(buf, DEFINE_SHA256_LENGTH, sigfile); if (verify_ret != 1) { if (verify_ret == 0) { printf("verifySignature: Invalid signature provided\n"); } else { printf("verifySignature: Signature verification process error\n"); } return false; } return true; }
3. 额外验证建议
- 确认签名是用对应证书的私钥生成的,且签名算法为
SHA256withRSA(和代码中的EVP_sha256()匹配)。 - 如果使用RSA-PSS签名,需要在
EVP_DigestVerifyInit时配置PSS参数,否则验证会失败。 - 可以添加OpenSSL错误日志打印(
ERR_print_errors_fp(stdout)),快速定位底层错误原因。
内容的提问来源于stack exchange,提问作者PRABHAKARAN
相关产品推荐
相关产品推荐

