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

OpenSSL KTLS发送端生效但接收端未启用的原因及解决方法

KTLS仅发送端(TX)生效,接收端(RX)未启用的解决问题

问题背景

客户端与服务端通过已建立的TCP连接,使用SSL_set_fd()启动TLS握手。服务端核心代码如下:

auto ctx = SSL_CTX_new(TLS_server_method());
SSL_CTX_set_options(ctx, SSL_OP_ENABLE_KTLS);
SSL_CTX_use_certificate_chain_file(ctx, "cert.pem");
SSL_CTX_use_PrivateKey_file(ctx, "priv.pem", SSL_FILETYPE_PEM);
auto ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
SSL_accept(ssl);   // 客户端执行SSL_connect()
std::cerr << BIO_get_ktls_send(SSL_get_wbio(ssl)) << "\n";
std::cerr << BIO_get_ktls_recv(SSL_get_rbio(ssl)) << "\n";

省略错误处理与证书校验后,发现KTLS仅TX端生效(输出1),RX端未启用(输出0)。通过strace可见OpenSSL仅设置了TLS_TX的setsockopt,未设置TLS_RX,内核tls模块已加载。

环境信息:Ubuntu 22.04.1 LTS,OpenSSL 3.0.2-ubuntu1.7,内核版本5.15.0-46-generic


解决方法

方法1:调整OpenSSL上下文配置,自动启用KTLS RX

OpenSSL 3.0.x中,KTLS RX的自动启用需要满足两个关键条件:使用内核支持的密码套件,且避免重协商干扰。修改服务端代码如下:

auto ctx = SSL_CTX_new(TLS_server_method());
// 启用KTLS并禁用重协商(重协商会破坏RX端的KTLS状态)
SSL_CTX_set_options(ctx, SSL_OP_ENABLE_KTLS | SSL_OP_NO_RENEGOTIATION);
// 指定内核原生支持的密码套件(优先选择AES-GCM系列)
SSL_CTX_set_cipher_list(ctx, "AES128-GCM-SHA256:AES256-GCM-SHA384");
SSL_CTX_use_certificate_chain_file(ctx, "cert.pem");
SSL_CTX_use_PrivateKey_file(ctx, "priv.pem", SSL_FILETYPE_PEM);

auto ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
SSL_accept(ssl);

// 再次检查KTLS状态
std::cerr << BIO_get_ktls_send(SSL_get_wbio(ssl)) << "\n";
std::cerr << BIO_get_ktls_recv(SSL_get_rbio(ssl)) << "\n";

修改后重新测试,OpenSSL应会自动调用setsockopt配置TLS_RX。

方法2:手动调用setsockopt配置KTLS RX

若自动配置失败,可在TLS握手完成后,从SSL会话中提取加密参数,手动设置TLS_RX选项。示例代码如下:

#include <linux/tls.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>

// 握手完成后执行
if (SSL_is_init_finished(ssl)) {
    SSL_SESSION *sess = SSL_get_session(ssl);
    const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
    const EVP_CIPHER *evp_cipher = SSL_CIPHER_get_cipher(cipher);
    
    // 仅处理内核支持的AES-GCM套件(根据实际使用的套件调整)
    if (EVP_CIPHER_type(evp_cipher) == EVP_CIPH_AES_GCM_MODE) {
        struct tls_crypto_info_aes_gcm rx_info;
        memset(&rx_info, 0, sizeof(rx_info));

        // 填充基础信息
        rx_info.info.version = SSL_version(ssl) == TLS1_3_VERSION ? TLS_1_3_VERSION : TLS_1_2_VERSION;
        rx_info.info.cipher_type = TLS_CIPHER_AES_GCM;
        rx_info.info.cipher_len = EVP_CIPHER_key_length(evp_cipher);
        rx_info.info.iv_len = EVP_CIPHER_iv_length(evp_cipher);
        rx_info.info.record_seq_len = 8;

        // 提取主密钥(注意:实际需根据TLS版本提取正确的密钥材料)
        unsigned char master_key[EVP_MAX_KEY_LENGTH];
        int master_key_len = SSL_SESSION_get_master_key(sess, master_key, sizeof(master_key));
        if (master_key_len > 0) {
            memcpy(rx_info.key, master_key, rx_info.info.cipher_len);
        }

        // 提取IV和盐(不同TLS版本的提取方式不同,TLS1.2和TLS1.3需分别处理)
        // 此处示例仅做框架,需根据实际协议版本补充完整参数提取逻辑

        // 设置TLS_RX选项
        if (setsockopt(sock, SOL_TLS, TLS_RX, &rx_info, sizeof(rx_info)) < 0) {
            perror("setsockopt TLS_RX failed");
        }
    }
}

注意:密钥、IV等参数的提取需严格遵循TLS协议规范,不同版本(TLS1.2/TLS1.3)的密钥派生逻辑不同,需对应调整代码。

额外检查项

  • 确认内核tls模块已加载:执行modprobe tls(若未加载)
  • 验证密码套件兼容性:执行cat /proc/crypto | grep tls查看内核支持的TLS算法
  • 升级OpenSSL补丁:执行apt update && apt upgrade openssl获取最新修复版本

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:40:26