You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在OpenSSL 3.0 EVP_PKEY API中仅初始化公钥?——EVP_PKEY_fromdata调用失败问题求解

修复RSA公钥构建失败的问题

我看了你的代码,发现几个关键问题导致EVP_PKEY_fromdata()调用失败,咱们一个个理清楚:

1. 参数名匹配错误

OSSL_PARAM_BLD_push_BN()里你传了key作为参数名,但RSA公钥的模数参数名明确是"n"——得和你提取参数时用的名称完全对应才行,这是最核心的错误。

2. 密钥类型指定错误

你调用EVP_PKEY_fromdata()时用了EVP_PKEY_KEYPAIR,但签名验证端只需要公钥,应该用EVP_PKEY_PUBLIC_KEY。这个标志会告诉OpenSSL你要创建的是公钥而非完整密钥对,符合你的场景需求。

3. 资源泄漏与错误处理不完整

  • 你创建的BIGNUM没有释放,会导致内存泄漏;
  • 错误分支里的资源(比如ctxparameters)没有完整清理,容易引发后续问题。

修正后的完整代码

void SetPublicKey(const std::vector<unsigned char> &n_bytes) {
    // 先释放旧密钥(如果存在)
    if (keys_) {
        EVP_PKEY_free(keys_);
        keys_ = nullptr;
    }

    OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
    if (!param_bld) {
        throw std::runtime_error("Failed to create OSSL_PARAM builder");
    }

    BIGNUM *n_bn = BN_bin2bn(n_bytes.data(), static_cast<int>(n_bytes.size()), nullptr);
    if (!n_bn) {
        OSSL_PARAM_BLD_free(param_bld);
        throw std::runtime_error("Failed to convert bytes to BIGNUM");
    }

    // 推送公钥模数参数,参数名必须是"n"
    if (!OSSL_PARAM_BLD_push_BN(param_bld, "n", n_bn)) {
        BN_free(n_bn);
        OSSL_PARAM_BLD_free(param_bld);
        throw std::runtime_error("Failed to push 'n' parameter");
    }

    // 推送默认公钥指数RSA_F4(0x10001)
    if (!OSSL_PARAM_BLD_push_long(param_bld, "e", RSA_F4)) {
        BN_free(n_bn);
        OSSL_PARAM_BLD_free(param_bld);
        throw std::runtime_error("Failed to push 'e' parameter");
    }

    OSSL_PARAM *parameters = OSSL_PARAM_BLD_to_param(param_bld);
    OSSL_PARAM_BLD_free(param_bld);
    BN_free(n_bn); // BIGNUM已被参数构建器复制,此处可释放

    if (!parameters) {
        throw std::runtime_error("Failed to create OSSL_PARAM array");
    }

    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
    if (!ctx) {
        OSSL_PARAM_free(parameters);
        throw std::runtime_error("Failed to create EVP_PKEY context");
    }

    // 初始化公钥构建上下文
    if (EVP_PKEY_fromdata_init(ctx) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        OSSL_PARAM_free(parameters);
        throw std::runtime_error("Failed to initialize fromdata context");
    }

    // 构建公钥,使用EVP_PKEY_PUBLIC_KEY类型
    if (EVP_PKEY_fromdata(ctx, &keys_, EVP_PKEY_PUBLIC_KEY, parameters) <= 0) {
        EVP_PKEY_CTX_free(ctx);
        OSSL_PARAM_free(parameters);
        throw std::runtime_error("Failed to create public key from parameters");
    }

    // 清理资源
    EVP_PKEY_CTX_free(ctx);
    OSSL_PARAM_free(parameters);
}

额外注意事项

  • 确保你提取的"n"是正确的大端字节序(BN_bn2bin()输出的就是大端,和BN_bin2bn()的输入要求一致);
  • 如果你的场景中公钥指数不是默认的RSA_F4,需要从原始密钥中提取"e"参数并传入,不要硬编码;
  • 所有OpenSSL资源(EVP_PKEYEVP_PKEY_CTXOSSL_PARAMBIGNUM)都要在错误路径中正确释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Anton Karton

火山引擎 最新活动