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

OpenSSL 3.1及以上版本基于私钥创建EVP_PKEY的方法

在OpenSSL 3.1中创建EVP_PKEY用于EVP_DigestSignInit的正确方式

OpenSSL 3.x开始废弃了直接操作RSA/EC等具体密钥结构并赋值到EVP_PKEY的API,取而代之的是算法无关的密钥读取API,直接从PEM/DER等格式加载出EVP_PKEY对象,无需中间转换。

核心替代方案:使用PEM_read_bio_PrivateKey直接加载私钥

这个函数是OpenSSL推荐的跨版本兼容方式,能直接从PEM格式读取私钥并返回EVP_PKEY*,完美适配EVP_DigestSignInit的需求。

完整代码示例

#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>

int sign_data(const unsigned char *data, size_t data_len,
              const char *private_key_path,
              unsigned char **signature, size_t *signature_len) {
    EVP_PKEY *pkey = NULL;
    EVP_MD_CTX *md_ctx = NULL;
    int ret = 0;

    // 1. 打开私钥文件并创建BIO
    BIO *bio = BIO_new_file(private_key_path, "r");
    if (!bio) {
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }

    // 2. 直接读取私钥到EVP_PKEY(自动识别密钥类型:RSA/EC等)
    pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
    if (!pkey) {
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }

    // 3. 初始化签名上下文
    md_ctx = EVP_MD_CTX_new();
    if (!md_ctx) {
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }

    // 4. 设置签名算法(比如SHA256withRSA)
    if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }

    // 5. 传入待签名数据
    if (EVP_DigestSignUpdate(md_ctx, data, data_len) != 1) {
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }

    // 6. 获取签名长度并分配内存
    if (EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) {
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }
    *signature = OPENSSL_malloc(*signature_len);
    if (!*signature) {
        goto cleanup;
    }

    // 7. 生成签名
    if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) != 1) {
        OPENSSL_free(*signature);
        *signature = NULL;
        goto cleanup;
    }

    ret = 1;

cleanup:
    // 释放资源
    BIO_free(bio);
    EVP_PKEY_free(pkey);
    EVP_MD_CTX_free(md_ctx);
    return ret;
}

关键说明

  • PEM_read_bio_PrivateKey会自动识别私钥的算法类型(RSA、ECDSA等),无需手动指定,返回的EVP_PKEY可以直接用于所有EVP层的签名/验签/加密操作。
  • 若需从内存中的PEM数据读取私钥,可使用BIO_new_mem_buf替代BIO_new_file。
  • 错误处理依赖ERR_print_errors_fp打印OpenSSL错误栈,方便定位问题。
  • 所有OpenSSL对象(BIO、EVP_PKEY、EVP_MD_CTX)使用后必须释放,避免内存泄漏。

进阶:使用OSSL_DECODER(OpenSSL 3.x原生API)

如果需要更灵活的密钥解码(如支持更多格式、自定义参数),可以用OpenSSL 3.x引入的OSSL_DECODER系列API,示例如下:

#include <openssl/decoder.h>

// 从文件读取私钥到EVP_PKEY
EVP_PKEY *load_private_key(const char *path) {
    EVP_PKEY *pkey = NULL;
    OSSL_DECODER_CTX *dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey,
                                                           OSSL_KEYMGMT_SELECT_KEYPAIR_PRIVATE,
                                                           "PEM", NULL, NULL);
    if (!dctx) {
        ERR_print_errors_fp(stderr);
        return NULL;
    }

    BIO *bio = BIO_new_file(path, "r");
    if (!bio) {
        ERR_print_errors_fp(stderr);
        OSSL_DECODER_CTX_free(dctx);
        return NULL;
    }

    if (OSSL_DECODER_from_bio(dctx, bio) != 1) {
        ERR_print_errors_fp(stderr);
        pkey = NULL;
    }

    BIO_free(bio);
    OSSL_DECODER_CTX_free(dctx);
    return pkey;
}

这个API是OpenSSL 3.x的现代接口,支持更丰富的密钥格式和配置,适合复杂场景。

内容的提问来源于stack exchange,提问作者Farrokh Farivar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:25:02