如何通过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
相关产品推荐
相关产品推荐

