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

OpenSSL命令行签名验证通过但C++代码验证失败求排查

OpenSSL API实现PSS签名验证失败排查

需求目标

需要用C++代码实现以下OpenSSL命令的功能:

openssl dgst -sha256 -verify public_key.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature signature.sig sw-description

现有代码

用户编写的C++代码如下:

#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/types.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>

bool verify_signature(const std::string& signature_file, const std::string& public_key_file, const std::string& data_file)
{
    // Read the signature file
    std::ifstream sig_file(signature_file, std::ios::binary);
    if (!sig_file.is_open()) {
        std::cerr << "Failed to open signature file: " << signature_file << std::endl;
        return false;
    }
    std::vector<unsigned char> signature((std::istreambuf_iterator<char>(sig_file)), std::istreambuf_iterator<char>());
    sig_file.close();

     
     BIO* publicKeyBio = BIO_new_file(public_key_file.c_str(), "rb");
    if (!publicKeyBio) {
        perror("Failed to open public key file");
        return false;
    }
    EVP_PKEY* pkey = PEM_read_bio_PUBKEY(publicKeyBio, nullptr, nullptr, nullptr);
   
    if (!pkey) {
        ERR_print_errors_fp(stderr);
         std::cout<<"22";
        return false;
    }
    
    
    // Read the data file
    std::ifstream data_file_stream(data_file, std::ios::binary);
    if (!data_file_stream.is_open()) {
        std::cerr << "Failed to open data file: " << data_file << std::endl;
        
        return false;
    }
    std::vector<unsigned char> data((std::istreambuf_iterator<char>(data_file_stream)), std::istreambuf_iterator<char>());
    data_file_stream.close();

    // Create a digest of the data using SHA-256
    EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
    if (!md_ctx) {
        std::cerr << "Failed to create message digest context" << std::endl;
       
        return false;
    }
    if (EVP_DigestInit_ex(md_ctx, EVP_sha256(), nullptr) != 1) {
        std::cerr << "Failed to initialize message digest context" << std::endl;
        EVP_MD_CTX_free(md_ctx);
      
        return false;
    }
    if (EVP_DigestUpdate(md_ctx, data.data(), data.size()) != 1) {
        std::cerr << "Failed to update message digest" << std::endl;
        EVP_MD_CTX_free(md_ctx);
      
        return false;
    }
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int digest_len;
    if (EVP_DigestFinal_ex(md_ctx, digest, &digest_len) != 1) {
        std::cerr << "Failed to finalize message digest" << std::endl;
        EVP_MD_CTX_free(md_ctx);
      
        return false;
    }
    
    
    EVP_MD_CTX_free(md_ctx);

    // Verify the signature using the public key and PSS padding scheme
   
    EVP_MD_CTX* verify_ctx = EVP_MD_CTX_new();
    if (!verify_ctx) {
        std::cerr << "Failed to create signature verification context" << std::endl;
        EVP_PKEY_free(pkey);
       
        return false;
    }
    if (EVP_DigestVerifyInit(verify_ctx, nullptr, EVP_sha256(), nullptr, pkey) != 1) {
        std::cerr << "Failed to initialize signature verification context" << std::endl;
        EVP_MD_CTX_free(verify_ctx);
        EVP_PKEY_free(pkey);
       
        return false;
    }
    

    // Verify the signature
    if(EVP_DigestVerifyFinal(verify_ctx, signature.data(), signature.size()) != 1) 
    {
      ERR_print_errors_fp(stderr);
        std::cerr << "Signature verification failed" << std::endl;
        EVP_MD_CTX_free(verify_ctx);
        EVP_PKEY_free(pkey);
       
        return false;
    } 
    EVP_MD_CTX_free(verify_ctx);
    EVP_PKEY_free(pkey);
   

    std::cout << "Signature verification succeeded" << std::endl;
    return true;
}

int main()
{
    std::string signature_file = "signature_direct.sig";
    std::string public_key_file = "public_key.pem";
    std::string data_file = "sw.swu";

    if (!verify_signature(signature_file, public_key_file, data_file)) {
        std::cerr << "Signature verification failed" << std::endl;
        return 1;
    }

    return 0;
}

问题现象

使用上述OpenSSL命令验证签名时结果正常,但用相同文件通过该代码验证时失败。

问题原因及修复

代码存在两个关键问题:

  1. 错误预计算数据摘要:EVP_DigestVerify系列函数会自动对输入数据计算哈希并验证签名,无需手动提前计算摘要。手动计算摘要后直接调用EVP_DigestVerifyFinal相当于对摘要的哈希进行验证,和命令行逻辑不符。
  2. 未配置PSS签名选项:命令行中指定了rsa_padding_mode:pss和rsa_pss_saltlen:-1(盐长度等于哈希长度),但代码中未设置这些参数,导致默认使用PKCS#1 v1.5 padding,和签名时的PSS模式不匹配。

修复后的代码

#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/types.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>

bool verify_signature(const std::string& signature_file, const std::string& public_key_file, const std::string& data_file)
{
    // Read the signature file
    std::ifstream sig_file(signature_file, std::ios::binary);
    if (!sig_file.is_open()) {
        std::cerr << "Failed to open signature file: " << signature_file << std::endl;
        return false;
    }
    std::vector<unsigned char> signature((std::istreambuf_iterator<char>(sig_file)), std::istreambuf_iterator<char>());
    sig_file.close();

    BIO* publicKeyBio = BIO_new_file(public_key_file.c_str(), "rb");
    if (!publicKeyBio) {
        perror("Failed to open public key file");
        return false;
    }
    EVP_PKEY* pkey = PEM_read_bio_PUBKEY(publicKeyBio, nullptr, nullptr, nullptr);
    BIO_free(publicKeyBio); // 补充释放漏处理的BIO资源
    if (!pkey) {
        ERR_print_errors_fp(stderr);
        return false;
    }
    
    // Read the data file
    std::ifstream data_file_stream(data_file, std::ios::binary);
    if (!data_file_stream.is_open()) {
        std::cerr << "Failed to open data file: " << data_file << std::endl;
        EVP_PKEY_free(pkey);
        return false;
    }
    std::vector<unsigned char> data((std::istreambuf_iterator<char>(data_file_stream)), std::istreambuf_iterator<char>());
    data_file_stream.close();

    // 创建验证上下文并配置PSS选项
    EVP_MD_CTX* verify_ctx = EVP_MD_CTX_new();
    if (!verify_ctx) {
        std::cerr << "Failed to create signature verification context" << std::endl;
        EVP_PKEY_free(pkey);
        return false;
    }

    // 设置PSS填充模式和盐长度参数
    OSSL_PARAM params[3] = {0};
    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_RSA_PADDING_MODE, "pss", 0);
    params[1] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, const_cast<int*>(&(int){-1}));
    params[2] = OSSL_PARAM_construct_end();

    // 初始化验证上下文
    if (EVP_DigestVerifyInit(verify_ctx, nullptr, EVP_sha256(), nullptr, pkey) != 1) {
        std::cerr << "Failed to initialize signature verification context" << std::endl;
        EVP_MD_CTX_free(verify_ctx);
        EVP_PKEY_free(pkey);
        return false;
    }

    // 应用PSS参数配置
    if (EVP_MD_CTX_set_params(verify_ctx, params) != 1) {
        std::cerr << "Failed to set PSS verification parameters" << std::endl;
        EVP_MD_CTX_free(verify_ctx);
        EVP_PKEY_free(pkey);
        return false;
    }

    // 传入原始数据进行哈希计算和验证
    if (EVP_DigestVerifyUpdate(verify_ctx, data.data(), data.size()) != 1) {
        std::cerr << "Failed to update verification context with data" << std::endl;
        EVP_MD_CTX_free(verify_ctx);
        EVP_PKEY_free(pkey);
        return false;
    }

    // 完成签名验证
    int verify_result = EVP_DigestVerifyFinal(verify_ctx, signature.data(), signature.size());
    if (verify_result != 1) {
        if (verify_result == 0) {
            std::cerr << "Signature verification failed: invalid signature" << std::endl;
        } else {
            ERR_print_errors_fp(stderr);
            std::cerr << "Signature verification encountered an error" << std::endl;
        }
        EVP_MD_CTX_free(verify_ctx);
        EVP_PKEY_free(pkey);
        return false;
    }

    EVP_MD_CTX_free(verify_ctx);
    EVP_PKEY_free(pkey);

    std::cout << "Signature verification succeeded" << std::endl;
    return true;
}

int main()
{
    std::string signature_file = "signature_direct.sig";
    std::string public_key_file = "public_key.pem";
    std::string data_file = "sw.swu";

    if (!verify_signature(signature_file, public_key_file, data_file)) {
        std::cerr << "Signature verification failed" << std::endl;
        return 1;
    }

    return 0;
}

关键修改点说明

  • 删除了手动计算数据摘要的代码块,改用EVP_DigestVerifyUpdate直接传入原始数据,由OpenSSL自动处理哈希计算。
  • 添加了OSSL_PARAM结构体配置PSS填充模式和盐长度参数,并通过EVP_MD_CTX_set_params应用到验证上下文。
  • 补充了资源释放(比如BIO对象的释放),避免内存泄漏。
  • 细化了验证失败的错误信息区分(无效签名和内部错误)。

内容的提问来源于stack exchange,提问作者TANYA Malik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 23:14:50