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

如何通过C++调用OpenSSL TPM2 Provider实现TPM驻留密钥生成CSR?

用C++实现TPM驻留私钥生成CSR的方案

核心结论

  • 完全可以用C++实现你提供的bash脚本功能,基于OpenSSL TPM2 Provider即可。
  • 私钥无需提取到应用内:通过OSSL_STORE获取的EVP_PKEY只是一个代理句柄,实际私钥始终驻留在TPM中,所有签名操作都会委托给TPM执行,不会暴露私钥明文。

实现步骤与代码示例

以下是对应bash脚本功能的C++实现,关键逻辑和bash命令一一对应:

1. 初始化OpenSSL与加载Providers

需要先加载TPM2 Provider和默认Provider,确保密钥操作和CSR生成的功能都能正常工作。

2. 获取TPM驻留私钥(代理句柄)

你现有的GetPrivateKeyFromTPM函数是正确的,返回的EVP_PKEY是TPM Provider提供的封装对象,不会提取私钥到应用内存,所有密钥操作都会通过TPM完成。

3. 配置CSR参数并生成CSR

对应bash中的-config、-reqexts等参数,需要通过OpenSSL的X509_REQ相关API配置DN信息、扩展项等。

#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/store.h>
#include <openssl/provider.h>
#include <iostream>
#include <fstream>

// 获取TPM驻留私钥(代理句柄)
EVP_PKEY *GetPrivateKeyFromTPM(OSSL_LIB_CTX *libctx) {
    OSSL_STORE_CTX *storeCtx = NULL;
    // 替换为你的TPM密钥句柄
    storeCtx = OSSL_STORE_open_ex("handle:0x81005020", libctx, "?provider=tpm2", 
                                 NULL, NULL, NULL, NULL, NULL);
    if (!storeCtx) {
        std::cerr << "无法打开TPM存储上下文" << std::endl;
        return NULL;
    }

    EVP_PKEY *TPMpkey = NULL;
    while (!OSSL_STORE_eof(storeCtx)) {
        OSSL_STORE_INFO *info = OSSL_STORE_load(storeCtx);
        if (!info) continue;

        if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
            TPMpkey = OSSL_STORE_INFO_get1_PKEY(info);
            OSSL_STORE_INFO_free(info);
            break;
        }
        OSSL_STORE_INFO_free(info);
    }

    OSSL_STORE_close(storeCtx);
    return TPMpkey;
}

// 生成CSR核心函数
int GenerateCSRWithTPMKey(const char *csrPath, EVP_PKEY *tpmKey) {
    // 创建CSR上下文
    X509_REQ *req = X509_REQ_new();
    if (!req) {
        std::cerr << "创建X509_REQ失败" << std::endl;
        return -1;
    }

    // 设置CSR公钥(从TPM私钥关联的公钥获取,无需提取私钥)
    if (X509_REQ_set_pubkey(req, tpmKey) != 1) {
        std::cerr << "为CSR设置公钥失败" << std::endl;
        X509_REQ_free(req);
        return -1;
    }

    // 配置DN信息(对应openssl.conf中的[req_distinguished_name])
    X509_NAME *name = X509_REQ_get_subject_name(req);
    // 根据你的openssl.conf调整DN字段
    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, 
                               (const unsigned char *)"CN", -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, 
                               (const unsigned char *)"示例公司", -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, 
                               (const unsigned char *)"device.example.com", -1, -1, 0);

    // 配置扩展项(对应-reqexts v3_req)
    X509V3_CTX ctx;
    X509V3_set_ctx_nodb(&ctx);
    X509V3_set_ctx(&ctx, NULL, req, NULL, NULL, 0);
    // 根据你的v3_req配置添加扩展,这里以subjectAltName为例
    STACK_OF(X509_EXTENSION) *exts = sk_X509_EXTENSION_new_null();
    exts = X509V3_EXT_nconf(NULL, &ctx, "subjectAltName=DNS:device.example.com", exts);
    if (!exts) {
        std::cerr << "创建扩展项失败" << std::endl;
        X509_REQ_free(req);
        return -1;
    }
    X509_REQ_add_extensions(req, exts);
    sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);

    // 使用TPM私钥签名CSR(签名操作在TPM内部执行,私钥不会离开TPM)
    if (X509_REQ_sign(req, tpmKey, EVP_sha256()) == 0) {
        std::cerr << "用TPM密钥签名CSR失败" << std::endl;
        X509_REQ_free(req);
        return -1;
    }

    // 将CSR写入文件
    std::ofstream outFile(csrPath, std::ios::binary);
    if (!outFile.is_open()) {
        std::cerr << "打开输出文件失败" << std::endl;
        X509_REQ_free(req);
        return -1;
    }
    i2d_X509_REQ_fp(outFile, req);
    outFile.close();

    X509_REQ_free(req);
    return 0;
}

int main() {
    // 初始化OpenSSL库上下文
    OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
    if (!libctx) {
        std::cerr << "创建OSSL_LIB_CTX失败" << std::endl;
        return 1;
    }

    // 加载TPM2 Provider和默认Provider
    if (!OSSL_PROVIDER_load(libctx, "tpm2") || !OSSL_PROVIDER_load(libctx, "default")) {
        std::cerr << "加载Provider失败" << std::endl;
        OSSL_LIB_CTX_free(libctx);
        return 1;
    }

    // 获取TPM中的私钥(代理句柄)
    EVP_PKEY *tpmKey = GetPrivateKeyFromTPM(libctx);
    if (!tpmKey) {
        std::cerr << "获取TPM私钥失败" << std::endl;
        OSSL_LIB_CTX_free(libctx);
        return 1;
    }

    // 生成CSR
    if (GenerateCSRWithTPMKey("device.csr", tpmKey) != 0) {
        std::cerr << "生成CSR失败" << std::endl;
        EVP_PKEY_free(tpmKey);
        OSSL_LIB_CTX_free(libctx);
        return 1;
    }

    std::cout << "CSR生成成功:device.csr" << std::endl;

    // 释放资源
    EVP_PKEY_free(tpmKey);
    OSSL_LIB_CTX_free(libctx);
    return 0;
}

关键细节说明

  • 私钥安全性:GetPrivateKeyFromTPM返回的EVP_PKEY是TPM Provider提供的抽象对象,所有签名操作(比如X509_REQ_sign)都会通过Provider转发给TPM执行,私钥永远不会离开TPM的安全区域。
  • 对应bash参数:
    • -provider tpm2 -provider default:对应代码中加载两个Provider。
    • -key handle:$TPMHandle:对应OSSL_STORE_open_ex中的密钥句柄参数。
    • -config openssl.conf:对应代码中手动配置的DN和扩展项,可根据实际openssl.conf调整字段。
    • -reqexts v3_req:对应代码中添加的X509扩展项。

编译与运行

编译时链接OpenSSL库,命令示例:

g++ -o tpm_csr_generator tpm_csr_generator.cpp -lcrypto

运行前确保TPM2 Provider已正确安装,且TPM中的密钥句柄配置正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:17:10