我目前正在使用 OpenSSL3.0 编写一个 C++ 程序,该程序采用一串数据、EDCSA 签名,并使用存储在 .pem 文件中的 EC 公钥检查有效性。
目前我已尝试使用 EVP_DigestVerifyFinal() 但不太清楚如何使用新 API 导入我的 EC 密钥来执行验证,而不使用已弃用的函数。
我尝试过 OSSL_DECODER_from_data,将公钥作为 const unsigned char* 提供给它,但没能让它正常工作。
这是我的验证尝试
bool SignatureEcDSA::testVerif(const unsigned char* sig, const char* msg)
{
std::string key_ = "-----BEGIN PUBLIC "
"KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEy687PNFBHUW3KIYrgrdGtCY5bdDGvnbMj1v/"
"APR71dBv0mD3UXNULjAKSWVc4ahfIddpfX/i2N9ppMxVljk8BA==\n-----END PUBLIC KEY-----";
EVP_PKEY* key = nullptr;
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
//make key get from key_
BIO* keybio = BIO_new_mem_buf(key_.c_str(), -1);
if (keybio == nullptr) {
return false;
}
key = PEM_read_bio_PUBKEY(keybio, &key, nullptr, nullptr);
if (1 != EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, key))
return false;
if (1 != EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg))) {
return false;
}
if (1 == EVP_DigestVerifyFinal(mdctx, sig, strlen((const char*) sig))) {
return true;
}
}
这是我尝试制作OSSL_Decoder_from_data,它来自https://www.openssl.org/docs/man3.0/man3/OSSL_DECODER_from_bio.html
const char* key_ = "-----BEGIN PUBLIC "
"KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEy687PNFBHUW3KIYrgrdGtCY5bdDGvnbMj1v/"
"APR71dBv0mD3UXNULjAKSWVc4ahfIddpfX/i2N9ppMxVljk8BA==\n-----END PUBLIC KEY-----";
OSSL_DECODER_CTX* dctx;
EVP_PKEY* pkey = NULL;
const char* format = "PEM"; /* NULL for any format */
const char* structure = NULL; /* any structure */
const char* keytype = "EC"; /* NULL for any key */
auto* data = (const unsigned char*) key_;
size_t datalen = sizeof(key_);
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, format, structure, keytype,
OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, NULL, NULL);
if (dctx == NULL) {
std::cout << "No decoder context" << std::endl;
}
if (OSSL_DECODER_from_data(dctx, &data, &datalen)) {
std::cout << "OSSL_DECODER_from_data success" << std::endl;
}
else {
std::cout << "OSSL_DECODER_from_data failed" << std::endl; //fails here
}
OSSL_DECODER_CTX_free(dctx);
这里仍然使用字符串作为初始 EC 密钥,但我想从文件中打开它。
如果有人对我如何完成这项工作有任何提示,我将不胜感激!
谢谢
编辑:
我在测试之前尝试过这里提出的解决方案,但它使用 PEM_read_bio_EC_PUBKEY 已弃用的函数:ECDSA 签名验证:Go vs OpenSSL
来自 https://www.openssl.org/docs/man3.0/man3/PEM_read_bio_EC_PUBKEY.html:应用程序应该使用 OSSL_ENCODER_to_bio() 和 OSSL_DECODER_from_bio(),但也没有设法使其工作。
看来您使用 OSSL_DECODER_from_data 方法走在正确的轨道上,以避免不推荐使用的函数。但是,您的代码中存在一些问题可能会导致该问题。
密钥长度问题: 在 OSSL_DECODER_from_data 尝试中,您使用 sizeof(key_) 来确定密钥数据的长度。这将为您提供指针的大小,而不是数据的实际长度。您应该使用 strlen(key_) 代替。
释放后的内存所有权和使用: 使用 OSSL_DECODER_from_data 时,解码器获取输入数据的所有权,因此在将其传递给解码器后不应修改或释放它。但是,在您的代码中,您尝试在使用解码器上下文之前释放它,这可能会导致未定义的行为。
这是代码的更正版本:
const char* key_ = "-----BEGIN PUBLIC "
"KEY-----
\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEy687PNFBHUW3KIYrgrdGtCY5bdDGvnbMj1v/"
"APR71dBv0mD3UXNULjAKSWVc4ahfIddpfX/i2N9ppMxVljk8BA==\n-----
END PUBLIC KEY-----";
OSSL_DECODER_CTX* dctx;
EVP_PKEY* pkey = NULL;
const char* format = "PEM"; // NULL for any format
const char* structure = NULL; // any structure
const char* keytype = "EC"; // NULL for any key
const unsigned char* data = (const unsigned char*)key_;
size_t datalen = strlen(key_); // Use strlen instead of sizeof
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, format, structure, keytype,
OSSL_KEYMGMT_SELECT_KEYPAIR |
OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, NULL, NULL);
if (dctx == NULL) {
std::cout << "No decoder context" << std::endl;
}
if (OSSL_DECODER_from_data(dctx, &data, &datalen)) {
std::cout << "OSSL_DECODER_from_data success" << std::endl;
// You can use 'pkey' for verification here
} else {
std::cout << "OSSL_DECODER_from_data failed" << std::endl;
}
// 不要在这里释放 dctx,因为它拥有“数据”的所有权 OSSL_DECODER_CTX_free(dctx);
请确保使用从 OSSL_DECODER_from_data 调用中获取的 pkey 相应地调整验证码的其余部分。