You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 16:13:11