使用OpenSSL(Libcrypto)的ECDSA P-256/SHA-256验证时EVP_DigestVerifyFinal失败
Libcrypto ECDSA签名验证异常排查
背景:Python验证正常
我有一段可正常运行的Python代码,用于ECDSA签名验证:
#!/usr/bin/env python3 import hashlib from ecdsa import curves, VerifyingKey public_key_bytes = bytearray.fromhex("0303b2ce64bc207bdd8bc4df859187fcb686320d63ffa091410fc158fbb77980ea") public_key = VerifyingKey.from_string(public_key_bytes, curve=curves.NIST256p, hashfunc=hashlib.sha256) ds = bytearray.fromhex("F8CD88299FA4605800207BFEBEAC55024053F30F7C69B35C15E60800AC3B6FE3ED0639952F7B028D86867445961FFE94FB226BFF7006E0C451EE3F8728C177FB") m = bytearray.fromhex("8210492204E060610BDF26D77B5BF8C9CBFCF70422081475FD445DF0FF") print(public_key.verify(ds, m))
执行这段代码返回True,修改m、ds或公钥的任意字节都会触发验证错误,符合预期。其中m是原始消息,ds是对应的数字签名。
C++实现的问题
我用Libcrypto编写了两个C++函数,分别用于加载公钥和验证签名,但验证时始终失败。
加载公钥函数
EVP_PKEY* load_public_key(const unsigned char* public_key_bytes, size_t key_len) { EVP_PKEY* pkey = nullptr; EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr); if (!ctx) { std::cerr << "Failed to create EVP_PKEY_CTX" << std::endl; print_openssl_errors(); return nullptr; } if (EVP_PKEY_fromdata_init(ctx) <= 0) { std::cerr << "EVP_PKEY_fromdata_init failed" << std::endl; print_openssl_errors(); EVP_PKEY_CTX_free(ctx); return nullptr; } OSSL_PARAM params[3]; params[0] = OSSL_PARAM_construct_utf8_string("group", (char*)"P-256", 0); params[1] = OSSL_PARAM_construct_octet_string("pub", (void*)public_key_bytes, key_len); params[2] = OSSL_PARAM_construct_end(); if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) { std::cerr << "EVP_PKEY_fromdata failed" << std::endl; print_openssl_errors(); EVP_PKEY_CTX_free(ctx); return nullptr; } EVP_PKEY_CTX_free(ctx); return pkey; }
签名验证函数
bool verify_signature(EVP_PKEY* pkey, const unsigned char* msg, size_t msg_len, const unsigned char* sig, size_t sig_len) { EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); if (!mdctx) { std::cerr << "Failed to create EVP_MD_CTX" << std::endl; print_openssl_errors(); return false; } bool result = false; if (EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, pkey) <= 0) { std::cerr << "EVP_DigestVerifyInit failed" << std::endl; print_openssl_errors(); EVP_MD_CTX_free(mdctx); } if (EVP_DigestVerifyUpdate(mdctx, msg, msg_len) <= 0) { std::cerr << "EVP_DigestVerifyUpdate failed" << std::endl; print_openssl_errors(); EVP_MD_CTX_free(mdctx); } int verify_result = EVP_DigestVerifyFinal(mdctx, sig, sig_len); if (verify_result == 1) { result = true; // 验证成功 } else if (verify_result == 0) { std::cerr << "签名无效" << std::endl; } else { // 始终进入这个分支 std::cerr << "EVP_DigestVerifyFinal 执行失败" << std::endl; print_openssl_errors(); } EVP_MD_CTX_free(mdctx); return result; }
调用代码
std::array<std::uint8_t, 33> pk{0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D, 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, 0x79, 0x80, 0xEA}; std::array<std::uint8_t, 64> ds{0xF8, 0xCD, 0x88, 0x29, 0x9F, 0xA4, 0x60, 0x58, 0x00, 0x20, 0x7B, 0xFE, 0xBE, 0xAC, 0x55, 0x02, 0x40, 0x53, 0xF3, 0x0F, 0x7C, 0x69, 0xB3, 0x5C, 0x15, 0xE6, 0x08, 0x00, 0xAC, 0x3B, 0x6F, 0xE3, 0xED, 0x06, 0x39, 0x95, 0x2F, 0x7B, 0x02, 0x8D, 0x86, 0x86, 0x74, 0x45, 0x96, 0x1F, 0xFE, 0x94, 0xFB, 0x22, 0x6B, 0xFF, 0x70, 0x06, 0xE0, 0xC4, 0x51, 0xEE, 0x3F, 0x87, 0x28, 0xC1, 0x77, 0xFB}; std::array<std::uint8_t, 29> m{0x82, 0x10, 0x49, 0x22, 0x04, 0xE0, 0x60, 0x61, 0x0B, 0xDF, 0x26, 0xD7, 0x7B, 0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; // 加载公钥 EVP_PKEY* pkey = load_public_key(pk.data(), pk.size()); if (!pkey) { std::cerr << "加载公钥失败" << std::endl; return; } // 验证签名 bool is_valid = verify_signature(pkey, m.data(), m.size(), ds.data(), ds.size()); if (is_valid) { std::cout << "签名有效" << std::endl; } else { // 总是输出这个错误 std::cout << "签名无效" << std::endl; } // 释放公钥 EVP_PKEY_free(pkey);
问题现象
使用和Python示例相同的数据调用上述C++函数时,EVP_DigestVerifyFinal()始终返回-1,进入“EVP_DigestVerifyFinal 执行失败”分支,但EVP_get_error()没有返回错误信息。根据文档,返回-1表示签名格式或处理过程存在问题。我确认公钥加载正常,因为修改公钥字节会触发加载错误。
另外,若有精简代码生成更简洁示例的方法,也请告知。
内容的提问来源于stack exchange,提问作者Andre Buschermöhle
相关产品推荐
相关产品推荐

