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

代码生成的RSA签名与命令行签名不一致致验证失败求助

RSA签名验证失败问题排查与修复

问题现象

使用C++代码生成RSA签名后,命令行验证失败。对比代码与命令行生成的签名,发现前者缺失多个前导零(命令行签名中已高亮标注),该问题在CentOS和Kali系统上均复现。

签名生成代码

#include <iostream>
#include <fstream>
#include <vector>
#include <openssl/evp.h>
#include <openssl/pem.h>

int main() {
    FILE* pkey_file = fopen("private_key.pem", "r");
    if (!pkey_file) {
        std::cerr << "Error opening private key file\n";
        return 1;
    }

    EVP_PKEY* pkey = PEM_read_PrivateKey(pkey_file, nullptr, nullptr, nullptr);
    fclose(pkey_file);
    if (!pkey) {
        std::cerr << "Error reading private key\n";
        return 1;
    }

    std::ifstream input_file("data.txt", std::ios::binary);
    std::vector<char> buffer(std::istreambuf_iterator<char>(input_file), {});

    EVP_MD_CTX* ctx = EVP_MD_CTX_new();

    if (EVP_SignInit_ex(ctx, EVP_sha256(), nullptr) != 1) {
        std::cerr << "Error initializing signing context\n";
        EVP_PKEY_free(pkey);
        return 1;
    }

    if (EVP_SignUpdate(ctx, buffer.data(), buffer.size()) != 1) {
        std::cerr << "Error updating signing context\n";
        EVP_PKEY_free(pkey);
        return 1;
    }

    unsigned int len = EVP_PKEY_size(pkey);
    std::vector<unsigned char> signature(len);
    if (EVP_SignFinal(ctx, signature.data(), &len, pkey) != 1) {
        std::cerr << "Error finalizing signature\n";
        EVP_PKEY_free(pkey);
        return 1;
    }
    signature.resize(len);  // Resize signature to actual size

    for (unsigned char c : signature) {
        std::cout << std::hex << static_cast<int>(c);
    }
    std::cout << '\n';

    EVP_PKEY_free(pkey);
    EVP_MD_CTX_free(ctx);

    return 0;
}

相关密钥与数据

  • RSA私钥:
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAujWddIWHz7uYYqnV/aOLVYTYlXCgvozEn94WAPY7qAgD7CJH
lizWONX/snwVjqiPD+DITEPjXa5MGf3WSFJGYk9yxd8bzr9JJFWt2P3Sg8RoaegJ
9K5zrwQ1sdmBF7gBTN44TsEGYvNXoQ+faK+a5kjzRy5LNwEeAWb06dNAEsQsVvY8
LgxHoKIrLmaSTaDE0TW9bHiJ7xhAr2plPE86yHGVqt9ZR+J2OrGdkM0jAfrg9Zzb
niZkilUcaE1YqsVFfXff7Y0FPt2PoCd+bzncZjbyvR0mdLN5K5aVDHZYBheHR6xA
qQjkRQ8mrHLTotSEeJpTfjev0byEIRSLDD6SmwIDAQABAoH/LJlgFMfyz0aHdX3V
LaAWXFexm+kQ6iSEpRmOgbTGw+l0lEOnp5Ojl0Wcm71Y+aoxhVsbtC9XSwgxqVXP
BV2nET7CEtFdOppkXGGlsC8I+BaoTnX6Y8huqZM8m2bth32XvDv0o4JEHX40QaoQ
Q0CL5UzhecFMu1+EmSj5y49GNimyiInVv94sw3v6+dhNi8SRDhePSoAK4bklT8aM
k18qYQHOsv8cV4N4WssWyguU7wmuVsPztJtkx+MVh1DXVtRQ5QCW3X0/dpYIcp5x
LBGXSw4+sML0m4vZY/ogJcVh2f0xTLbZcxQFfDt1AJPGx0J8C4LfeRoDkmL7BsmO
AmPhAoGBAMVenorDu0L6zKZSnM3QIMK+kG82y7tqxCVFMc1buvKWnEad4BLpMYtK
hWlsP6DyTJ8Pql79997k76LCVhQ9t66DMgRzfrUcQL98qO9LFECWxCUa7+7E3mS6
kRFtzv+XbsWr4YHFRYQQnxvrdD1m6QO2kicv/hr4T6AjxTM6SysjAoGBAPGGTBr+
KqlvSxWxtQh6/N2OdG4+z0po9Isir45Oi+dfcgC9k7y0llx5pvO5ZbrwIGvvqQbX
Cxf6klolMgXJWGIpaEe4kGPnWf8B9T2FZ32Q8TjIiehlErLUhW2rc11brpg9pLtf
i6ApVCQLdiaIm7fuF24zPGm3EPLUWvstsE4pAoGBALCNuKMG/j+nXGQ4KF6PnI64
qzlhOhQ4KKfyYt+B/BS/l6Iv3d7szU395qPsmmqd9N6gw9Xft9ii0J622wuWPfaV
E6wZqLHYSS9CE1ijv8UbWPCcTR846nwOzHBb2zyWORgIblyInGvL9piiIkQ/B5gz
vD/j1ZpIn+nvtZrivZUjAoGAcL+i048HV2VvBceZnUXcuYkA7V5A4pmwXytoDOS6
Bt7Vl2boitU6xBy+RVStGuFyITEWFO1abm042QA4I99kdR6jYafVL+4WYycE1x1p
vIJnOXzUdDqKyhTP3OyGxHRCQyuztQzbX38rmlAHbeGj3+8z3OvqLeeZ7+IWPt+L
SFECgYAwuWwv8akZ2hmeYu/ehYhj7UwigLv+uS0XkKlpXsiLjj99gHAj8rGZn5Op
XnJ+ABDKRnZzOjUbERxXaWW8Xsls7N5p30PgGcOKcCCxU84pw0Vxqy56xgF2Mab5
X1zgQAZ7fnIrkCJzrBERkgr5Wet1y7XAjzYYBEHXbM9nBQ6Pjg==
-----END RSA PRIVATE KEY-----
  • 待签名文件data.txt内容:111111
  • RSA公钥:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAujWddIWHz7uYYqnV/aOL
VYTYlXCgvozEn94WAPY7qAgD7CJHlizWONX/snwVjqiPD+DITEPjXa5MGf3WSFJG
Yk9yxd8bzr9JJFWt2P3Sg8RoaegJ9K5zrwQ1sdmBF7gBTN44TsEGYvNXoQ+faK+a
5kjzRy5LNwEeAWb06dNAEsQsVvY8LgxHoKIrLmaSTaDE0TW9bHiJ7xhAr2plPE86
yHGVqt9ZR+J2OrGdkM0jAfrg9ZzbniZkilUcaE1YqsVFfXff7Y0FPt2PoCd+bznc
ZjbyvR0mdLN5K5aVDHZYBheHR6xAqQjkRQ8mrHLTotSEeJpTfjev0byEIRSLDD6S
mwIDAQAB
-----END PUBLIC KEY-----

签名对比

  • 代码生成的十六进制签名:
9751cc93bbd0486efe89abe4872d52f6d71886815af1c2f3d13f92e3a4c636de86bdcfb9512ec4538a48f4798965f6c91845e7efc7a8bf20182bbd8d5d9632123dc79caa329ffa33eb2b333eaff64ea137e83e3deac3bcac6c177056deab198cfd548799c6adad3cde3359ba461185c82b2c25c574c31f473e95c8586826cf5991ae1687fdcb7c0ec2eb83510f494715994c21ffcca1b4af7e45fe7b678c6774c437b9b18af1f3c83f2a284961fc2e0ff9aebf1c9c8e373bcf8adc674c41c5b81f357988b39513d63da564d8d349d964d8dde879f66578f8fb2847c4e1c45ed287bca8b6344a6785b384f3d9b153e5ef5626db99056
  • 命令行生成的十六进制签名(缺失的0已高亮):
9751cc93bbd0486efe89abe48702d52f6d71886815af1c2f3d13f92e3a4c636de86bdcfb9512ec**0**4538a**0**48f4798965f6c91845e7efc7a8b**0**f20182bbd8d5d9632123dc79caa329ffa33**0**eb2b333eaff064ea137**0**e83**0**e3deac3bcac6c177056deab19**0**8cf**0**d548799c6ad**0**a**0**d3cde3359ba461185c82b2c25c574c31f473e95**0**c8586826cf599**0**1ae1687fdcb**0**7c0ec2eb83510f494715994c21ffcca1b4af7e45fe7b678c6774c437b9b18af1f3c83f2a284961fc2**0**e0**0**ff9aebf1c9**0**c8e373bcf8adc674c41**0**c5b81f357988b39513d63da564d8d349d964d**0**8dde879f66578f8fb2847c4e1c45e**0**d287bca8b6344a6785b384f3d9b153e5ef5626db99056

问题原因

代码输出签名十六进制时,使用std::cout << std::hex << static_cast<int>(c);的方式,当字节值小于0x10时(如0x02),仅输出单个字符2而非标准两位字符02。这导致生成的十六进制字符串长度不足,验证时解析出的签名数据与实际生成的不符,最终验证失败。

修复方法

修改签名输出逻辑,确保每个字节都输出两位十六进制字符,不足时补前导零。有两种实现方式:

方式一:使用标准库格式控制

新增<iomanip>头文件,利用格式标志补零:

#include <iomanip>  // 新增头文件

// ... 其他代码不变 ...

for (unsigned char c : signature) {
    std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
}
std::cout << '\n';

方式二:手动转换十六进制

直接处理每个字节的高低位,确保两位输出:

for (unsigned char c : signature) {
    // 输出高位十六进制
    std::cout << "0123456789abcdef"[(c >> 4) & 0x0F];
    // 输出低位十六进制
    std::cout << "0123456789abcdef"[c & 0x0F];
}
std::cout << '\n';

修改后重新编译运行,生成的十六进制签名会包含完整前导零,与命令行生成的签名一致,即可正常通过验证。

内容的提问来源于stack exchange,提问作者周宁音

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:42:00