I Am currently writing a C++ program with OpenSSL3.0 which takes a string of data, EDCSA signature, and checks the validity with EC public Key stored in a .pem file.
Currently I have tried to use EVP_DigestVerifyFinal() but can't quite figure out how I can, with the new API, import my EC key to perform the verificaton, without the use of deprecated functions.
I Have tried the OSSL_DECODER_from_data, by giving it the public key as const unsigned char* but had no luck on making it quite work.
Here is my attempt at the the verification
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;
}
}
And here is my attempt at making the OSSL_Decoder_from_data which comes from 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);
It still uses a string as the initial EC key here, but I would like to open it from a file. (solved by BIO_new_file()!)
If anyone has a hint on how i could make this work it would be greatly appreciated !
Thanks
EDIT:
I have tried the solution proposed here prior to my tests, but it uses PEM_read_bio_EC_PUBKEY deprecated function : ECDSA signature verification: Go vs OpenSSL
From https://www.openssl.org/docs/man3.0/man3/PEM_read_bio_EC_PUBKEY.html : Applications should use OSSL_ENCODER_to_bio() and OSSL_DECODER_from_bio(), but didn't manage to make it work either.
EDIT 2:
Added the verification along with firstly corrected code, still fails at the same place. OSSL_DECODER_from_data returns 0.
bool SignatureEcDSA::testVerif(const unsigned char* sig, const char* msg)
{
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;
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
if (1 != EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, pkey))
return false;
if (1 != EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg))) {
return false;
}
if (1 == EVP_DigestVerifyFinal(mdctx, sig, strlen((const char*) sig))) {
return true;
}
}
else {
std::cout << "OSSL_DECODER_from_data failed" << std::endl; //fails here
}
return false;
}
The problem was in your selection
parameter. Passing either 0
or OSSL_KEYMGMT_SELECT_PUBLIC_KEY
allows the call of OSSL_DECODER_from_data
(or OSSL_DECODER_from_bio
) to pass.