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

使用OpenSSL库(C语言)如何在psk_server_cb阶段获取选定加密套件?

获取PSK服务器回调中的选定加密套件

我之前也碰到过一模一样的问题——在psk_server_cb阶段需要拿到加密套件来生成PSK,但SSL_get_current_cipher()因为会话还没完全建立总是返回NULL。下面是两种可行的解决方案,按推荐程度排序:

方法一:利用握手回调+SSL ex_data存储(推荐,兼容性好)

这个方法的核心是在**握手流程中更早的阶段(加密套件已选定但PSK回调未触发时)**捕获加密套件信息,然后存储到SSL对象的扩展数据中,供PSK回调使用:

  1. 注册SSL ex_data索引:
    首先为SSL对象创建一个专属的扩展数据索引,用来存储加密套件指针:

    static int ssl_cipher_exdata_idx = -1;
    
    // 在程序初始化时调用一次
    void init_exdata_index() {
        ssl_cipher_exdata_idx = SSL_get_ex_new_index(0, "selected_cipher", NULL, NULL, NULL);
        if (ssl_cipher_exdata_idx == -1) {
            // 处理索引注册失败的错误逻辑
        }
    }
    
  2. 设置握手回调函数:
    注册一个握手回调,当握手进入SSL_ST_ACCEPT状态时,加密套件已经选定,此时可以安全获取并存储:

    int handshake_callback(SSL *ssl, int where, int ret) {
        if ((where & SSL_ST_ACCEPT) && ret == 1) {
            const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
            if (cipher != NULL) {
                SSL_set_ex_data(ssl, ssl_cipher_exdata_idx, (void*)cipher);
            }
        }
        return ret;
    }
    
    // 在创建SSL_CTX时绑定回调
    SSL_CTX_set_handshake_callback(ctx, handshake_callback);
    
  3. 在psk_server_cb中获取加密套件:
    现在在PSK回调里,直接从ex_data中取出之前存储的加密套件:

    unsigned int psk_server_cb(SSL *ssl, const char *hint, char *identity,
                               unsigned int max_identity_len, unsigned char *psk,
                               unsigned int max_psk_len) {
        const SSL_CIPHER *selected_cipher = SSL_get_ex_data(ssl, ssl_cipher_exdata_idx);
        if (selected_cipher != NULL) {
            const char *cipher_name = SSL_CIPHER_get_name(selected_cipher);
            // 在这里使用cipher_name生成对应的PSK
            // ...
        }
        // 处理PSK生成和身份返回的逻辑
        // ...
    }
    

方法二:直接访问SSL内部结构(不推荐,兼容性差)

如果你的OpenSSL版本固定(比如1.1.0或某一特定版本),可以直接访问SSL对象的内部字段来获取选定的加密套件,但这种方法会因为OpenSSL版本更新而失效,仅作为应急方案:

// 仅适用于特定OpenSSL版本,示例为1.1.0+
#include <openssl/ssl.h>
#include <openssl/ssl3.h>

unsigned int psk_server_cb(SSL *ssl, const char *hint, char *identity,
                           unsigned int max_identity_len, unsigned char *psk,
                           unsigned int max_psk_len) {
#ifdef OPENSSL_VERSION_NUMBER >= 0x10100000L
    const SSL_CIPHER *cipher = ssl->s3->tmp.new_cipher;
#else
    // 旧版本可能需要访问不同的字段,比如ssl->session->cipher
    const SSL_CIPHER *cipher = ssl->session->cipher;
#endif
    if (cipher != NULL) {
        // 使用加密套件生成PSK
    }
    // ...
}

注意:这种方法完全依赖OpenSSL的内部实现,升级库后大概率会出现崩溃或逻辑错误,所以优先选择方法一。

关键说明

  • psk_server_cb触发时,服务器已经完成了加密套件的选定(从ClientHello的候选列表中选出),只是会话还没完成握手,所以SSL_get_current_cipher()还未被初始化,但握手回调的SSL_ST_ACCEPT阶段已经可以拿到这个信息。
  • 确保ex_data索引只初始化一次,避免重复注册导致错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:59:41