You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:15:51