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

如何将TPM特定的TPMT_PUBLIC数据结构转换为OpenSSL的EVP_PKEY格式ECC公钥?

如何将TPM特定的TPMT_PUBLIC数据结构转换为OpenSSL的EVP_PKEY格式ECC公钥?

我刚好处理过类似的需求,要把TPM返回的TPMT_PUBLIC结构里的ECC公钥转成OpenSSL能直接用的EVP_PKEY,下面是一步步的实现思路和代码示例,亲测可行。

核心思路拆解

要完成转换,核心是从TPMT_PUBLIC里提取ECC公钥的三个关键信息:ECC曲线类型、公钥的x坐标、公钥的y坐标,再用OpenSSL的libcrypto API把这些信息组装成EVP_PKEY。

步骤1:确认公钥类型是ECC

首先要检查TPMT_PUBLIC的type字段是否为TPM_ALG_ECC,如果不是的话,说明这不是ECC公钥,直接跳过后续步骤。

步骤2:映射TPM曲线ID到OpenSSL的NID

TPM的ECC曲线由TPMT_PUBLIC->parameters.eccDetail.curveID(TPMS_ECC_PARMS结构里的curveID)定义,我们需要把它转换成OpenSSL识别的NID(数值标识符)。常见的映射关系如下:

  • TPM_ECC_NIST_P256 → NID_X9_62_prime256v1
  • TPM_ECC_NIST_P384 → NID_secp384r1
  • TPM_ECC_NIST_P521 → NID_secp521r1
    如果你的TPM支持其他曲线,需要查OpenSSL的对应NID补充映射。

步骤3:提取公钥的x、y坐标

TPMT_PUBLIC的publicValue.ecc是TPMS_ECC_POINT结构,里面的x和y都是TPM2B_ECC_PARAMETER类型——这是一个带长度字段的字节数组。我们需要取出x.t.buffer和y.t.buffer里的原始字节数据(TPM存储的坐标是大端字节序,刚好符合OpenSSL的要求),长度对应x.t.size和y.t.size。

步骤4:用OpenSSL API组装EVP_PKEY

下面是具体的C代码实现,包含错误处理和内存管理:

#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <tss2/tss2_tpm2_types.h>

EVP_PKEY* tpmt_public_to_evp_pkey(const TPMT_PUBLIC* tpm_public) {
    if (!tpm_public || tpm_public->type != TPM_ALG_ECC) {
        fprintf(stderr, "输入不是ECC类型的公钥\n");
        return NULL;
    }

    // 1. 映射TPM曲线ID到OpenSSL NID
    int nid = -1;
    switch (tpm_public->parameters.eccDetail.curveID) {
        case TPM_ECC_NIST_P256:
            nid = NID_X9_62_prime256v1;
            break;
        case TPM_ECC_NIST_P384:
            nid = NID_secp384r1;
            break;
        case TPM_ECC_NIST_P521:
            nid = NID_secp521r1;
            break;
        // 其他曲线请自行添加映射
        default:
            fprintf(stderr, "不支持该ECC曲线\n");
            return NULL;
    }

    // 2. 创建EC_KEY结构
    EC_KEY* ec_key = EC_KEY_new_by_curve_nid(nid);
    if (!ec_key) {
        fprintf(stderr, "创建EC_KEY失败\n");
        return NULL;
    }

    const EC_GROUP* group = EC_KEY_get0_group(ec_key);
    if (!group) {
        fprintf(stderr, "获取EC_GROUP失败\n");
        EC_KEY_free(ec_key);
        return NULL;
    }

    // 3. 转换x、y字节数组为BIGNUM
    BIGNUM* x = BN_bin2bn(tpm_public->publicValue.ecc.x.t.buffer, 
                          tpm_public->publicValue.ecc.x.t.size, NULL);
    BIGNUM* y = BN_bin2bn(tpm_public->publicValue.ecc.y.t.buffer, 
                          tpm_public->publicValue.ecc.y.t.size, NULL);
    if (!x || !y) {
        fprintf(stderr, "坐标转BIGNUM失败\n");
        BN_free(x);
        BN_free(y);
        EC_KEY_free(ec_key);
        return NULL;
    }

    // 4. 创建EC_POINT并设置坐标
    EC_POINT* point = EC_POINT_new(group);
    if (!point) {
        fprintf(stderr, "创建EC_POINT失败\n");
        BN_free(x);
        BN_free(y);
        EC_KEY_free(ec_key);
        return NULL;
    }

    if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, NULL)) {
        fprintf(stderr, "设置仿射坐标失败\n");
        EC_POINT_free(point);
        BN_free(x);
        BN_free(y);
        EC_KEY_free(ec_key);
        return NULL;
    }

    // 5. 将EC_POINT关联到EC_KEY
    if (!EC_KEY_set_public_key(ec_key, point)) {
        fprintf(stderr, "给EC_KEY设置公钥失败\n");
        EC_POINT_free(point);
        BN_free(x);
        BN_free(y);
        EC_KEY_free(ec_key);
        return NULL;
    }

    // 6. 转换EC_KEY为EVP_PKEY
    EVP_PKEY* evp_pkey = EVP_PKEY_new();
    if (!evp_pkey || !EVP_PKEY_set1_EC_KEY(evp_pkey, ec_key)) {
        fprintf(stderr, "EC_KEY转EVP_PKEY失败\n");
        EVP_PKEY_free(evp_pkey);
        EC_POINT_free(point);
        BN_free(x);
        BN_free(y);
        EC_KEY_free(ec_key);
        return NULL;
    }

    // 释放中间结构(EVP_PKEY会持有EC_KEY的引用,所以EC_KEY可以释放)
    EC_POINT_free(point);
    BN_free(x);
    BN_free(y);
    EC_KEY_free(ec_key);

    return evp_pkey;
}

注意事项

  • 内存管理:使用完EVP_PKEY后,记得调用EVP_PKEY_free(evp_pkey)释放内存,避免泄漏。
  • 曲线兼容性:确保你的OpenSSL版本支持TPM使用的ECC曲线,有些小众曲线可能需要额外编译OpenSSL支持。
  • 错误处理:代码里的每个API调用都加了错误检查,实际项目中可以根据需求调整错误日志的输出方式。

备注:内容来源于stack exchange,提问作者user24805910

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 10:48:07