代码生成的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,提问作者周宁音
相关产品推荐
相关产品推荐

