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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 05:13:17