OpenSSL引擎/Provider能否将TLS_PSK_WITH_NULL_SHA256密钥委托至TZ/TPM?
问题解答
疑问1:OpenSSL 1.1.1引擎能否用于TLS_PSK_WITH_NULL_SHA256?
不能。OpenSSL 1.1.1的PSK握手逻辑依赖SSL_CTX_set_psk_client_callback这类回调函数,要求回调返回明文PSK密钥到用户态内存供OpenSSL使用。即便开发自定义引擎,也无法绕开明文传递的要求——引擎核心作用是加密算法硬件加速,而非密钥全生命周期隔离,因此无法满足密钥不暴露在Linux用户态的约束。
疑问2:OpenSSL 3.x Provider能否实现密钥委托?
可以。OpenSSL 3.x的Provider架构是模块化设计,允许开发者自定义密钥存储、提取和操作逻辑,完全能将PSK密钥的访问与使用委托到TrustZone或TPM,全程不在用户态内存暴露明文密钥。
代码示例
1. 自定义PSK Provider核心实现(简化版)
示例实现了最小化Provider框架,模拟TrustZone/TPM接口获取PSK(实际场景需替换为硬件厂商SDK调用):
#include <openssl/core_dispatch.h> #include <openssl/core_names.h> #include <openssl/params.h> #include <openssl/ssl.h> // 模拟从安全硬件获取PSK的函数 static int secure_hw_get_psk(unsigned char *psk_out, size_t *psk_len) { // 实际场景:调用TrustZone SMC或TPM SDK接口 static const unsigned char demo_psk[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10}; if (*psk_len < sizeof(demo_psk)) { *psk_len = sizeof(demo_psk); return 0; } memcpy(psk_out, demo_psk, sizeof(demo_psk)); *psk_len = sizeof(demo_psk); return 1; } // 密钥管理接口实现 static void *psk_keymgmt_new(void *prov_ctx) { return OPENSSL_zalloc(sizeof(int)); } static void psk_keymgmt_free(void *keymgmt_ctx) { OPENSSL_free(keymgmt_ctx); } static int psk_keymgmt_get_params(void *keymgmt_ctx, OSSL_PARAM params[]) { OSSL_PARAM *p; unsigned char psk_buf[32]; size_t psk_len = sizeof(psk_buf); if (!secure_hw_get_psk(psk_buf, &psk_len)) return 0; p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PSK_VALUE); if (p != NULL && !OSSL_PARAM_set_octet_string(p, psk_buf, psk_len)) return 0; p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PSK_LEN); if (p != NULL && !OSSL_PARAM_set_size_t(p, psk_len)) return 0; return 1; } // Provider调度表 static const OSSL_DISPATCH psk_dispatch_table[] = { {OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))psk_keymgmt_new}, {OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))psk_keymgmt_free}, {OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))psk_keymgmt_get_params}, {0, NULL} }; // Provider初始化 static int psk_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, const OSSL_DISPATCH **out, void **prov_ctx) { *out = psk_dispatch_table; *prov_ctx = NULL; return 1; } // Provider入口点 OSSL_PROVIDER_FUNC void OSSL_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, const OSSL_DISPATCH **out, void **prov_ctx) { *out = NULL; *prov_ctx = NULL; if (!psk_provider_init(handle, in, out, prov_ctx)) return; }
编译为动态库:
gcc -fPIC -shared -o libpsk_secure_prov.so psk_provider.c -lcrypto -lssl
2. TLS客户端使用自定义Provider的示例
#include <openssl/ssl.h> #include <openssl/provider.h> #include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> int main() { // 加载自定义Provider OSSL_PROVIDER *psk_prov = OSSL_PROVIDER_load(NULL, "./libpsk_secure_prov.so"); if (!psk_prov) { fprintf(stderr, "Failed to load PSK provider\n"); return 1; } // 创建SSL上下文并指定目标 cipher SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) { fprintf(stderr, "Failed to create SSL context\n"); OSSL_PROVIDER_unload(psk_prov); return 1; } if (!SSL_CTX_set_cipher_list(ctx, "TLS_PSK_WITH_NULL_SHA256")) { fprintf(stderr, "Failed to set cipher list\n"); SSL_CTX_free(ctx); OSSL_PROVIDER_unload(psk_prov); return 1; } // 约定的PSK身份标识 const char *psk_identity = "arm_tls_client_001"; // 设置PSK回调,从Provider获取密钥 SSL_CTX_set_psk_client_callback(ctx, [](SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) -> unsigned int { EVP_PKEY *psk_key = EVP_PKEY_new(); if (!psk_key || !EVP_PKEY_set_type_str(psk_key, "PSK", 0)) { EVP_PKEY_free(psk_key); return 0; } OSSL_PARAM params[3] = { OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PSK_VALUE, psk, max_psk_len), OSSL_PARAM_size_t(OSSL_PKEY_PARAM_PSK_LEN, NULL), OSSL_PARAM_END }; size_t psk_len; params[1].data = &psk_len; if (!EVP_PKEY_get_params(psk_key, params)) { EVP_PKEY_free(psk_key); return 0; } // 复制身份标识 strncpy(identity, psk_identity, max_identity_len - 1); identity[max_identity_len - 1] = '\0'; EVP_PKEY_free(psk_key); return (unsigned int)psk_len; }); // 建立TCP连接(示例连接本地服务端,实际替换为目标地址) int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv_addr = {0}; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(4433); inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr); if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { fprintf(stderr, "Connection failed\n"); SSL_CTX_free(ctx); OSSL_PROVIDER_unload(psk_prov); close(sockfd); return 1; } // 完成SSL握手 SSL *ssl = SSL_new(ctx); SSL_set_fd(ssl, sockfd); if (SSL_connect(ssl) <= 0) { fprintf(stderr, "SSL handshake failed\n"); SSL_free(ssl); SSL_CTX_free(ctx); OSSL_PROVIDER_unload(psk_prov); close(sockfd); return 1; } printf("SSL handshake successful\n"); // 后续数据传输逻辑... // 资源清理 SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); OSSL_PROVIDER_unload(psk_prov); close(sockfd); return 0; }
编译客户端:
g++ -o tls_psk_client client.cpp -lcrypto -lssl
关键说明
- 自定义Provider中的
secure_hw_get_psk函数需替换为实际的TrustZone/TPM硬件调用,确保密钥直接从安全域获取,不经过用户态明文存储。 - 客户端通过
EVP_PKEY_get_params从Provider提取密钥,全程不在用户态内存留存明文PSK。
内容的提问来源于stack exchange,提问作者ruben2020
相关产品推荐
相关产品推荐

