我正在使用 openssl api 编写 C++ 代码来执行以下功能
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;
}
if (EVP_PKEY_CTX_set_rsa_padding(EVP_MD_CTX_pkey_ctx(verify_ctx), RSA_PKCS1_PSS_PADDING) <= 0) {
std::cerr << "Failed to set RSA-PSS padding in signature verification context" << std::endl;
EVP_MD_CTX_free(verify_ctx);
EVP_PKEY_free(pkey);
return false;
}
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_MD_CTX_pkey_ctx(verify_ctx), -1) <= 0) {
std::cerr << "Failed to set RSA-PSS salt length in signature verification context" << std::endl;
EVP_MD_CTX_free(verify_ctx);
EVP_PKEY_free(pkey);
return false;
}
if (EVP_DigestVerifyUpdate(verify_ctx, digest, digest_len) != 1) {
ERR_print_errors_fp(stderr);
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 命令时,签名验证正常,但是在 C++ 代码中使用相同的文件时,签名验证失败。
有人可以帮忙吗,我的代码哪里错了?
首先,
EVP_DigestVerifyUpdate
对数据本身进行操作,而不是对数据的哈希进行操作。所以你不应该对数据进行哈希处理。您应该直接将其传递给EVP_DigestVerifyUpdate
。所以第一步:
EVP_DigestVerifyUpdate
:if (EVP_DigestVerifyUpdate(verify_ctx, data.data(), data.size()) != 1) {
^^^^^^^^^^^^^^^^^^^^^^^^
接下来,
EVP_PKEY_CTX_set_*
方法采用EVP_PKEY_CTX
。编译器可能会警告您这一点,但您随后告诉编译器您通过强制转换知道自己在做什么:
if (EVP_PKEY_CTX_set_rsa_padding(EVP_MD_CTX_pkey_ctx(verify_ctx), RSA_PKCS1_PSS_PADDING) <= 0) {
EVP_MD_CTX
与 EVP_PKEY_CTX
不同。这只会破坏上下文。
相反,您需要依赖这样一个事实:如果您要求,
EVP_DigestVerifyInit
将返回其 PKEY 上下文:
EVP_PKEY_CTX* pkey_ctx = NULL;
if (EVP_DigestVerifyInit(verify_ctx, &pkey_ctx, EVP_sha256(), nullptr, pkey) != 1) {
有了
pkey_ctx
可用,您可以配置它:
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
^^^^^^^^
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) <= 0) {
^^^^^^^^
我还修改了
main()
中的文件名以匹配您在 openssl
命令中使用的名称。这样,这就是完整的代码:
/**
* Equivalent to:
openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem
echo testfile > sw-description
openssl dgst -sha256 -sign private_key.pem -out signature.sig -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 sw-description
openssl dgst -sha256 -verify public_key.pem -signature signature.sig -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 sw-description
*/
#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();
// 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;
}
EVP_PKEY_CTX* pkey_ctx = NULL;
if (EVP_DigestVerifyInit(verify_ctx, &pkey_ctx, 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;
}
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
std::cerr << "Failed to set RSA-PSS padding in signature verification context" << std::endl;
EVP_MD_CTX_free(verify_ctx);
EVP_PKEY_free(pkey);
return false;
}
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) <= 0) {
std::cerr << "Failed to set RSA-PSS salt length in signature verification context" << std::endl;
EVP_MD_CTX_free(verify_ctx);
EVP_PKEY_free(pkey);
return false;
}
if (EVP_DigestVerifyUpdate(verify_ctx, data.data(), data.size()) != 1) {
ERR_print_errors_fp(stderr);
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.sig";
std::string public_key_file = "public_key.pem";
std::string data_file = "sw-description";
if (!verify_signature(signature_file, public_key_file, data_file)) {
std::cerr << "Signature verification failed" << std::endl;
return 1;
}
return 0;
}