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

如何在C客户端的ClientHello消息中添加自定义TLS扩展?

TLS ClientHello自定义扩展数据缺失问题解决

问题根源

你的ext_add_cb回调存在两个致命错误:

  • 用栈上局部变量存储扩展数据,回调结束后内存被回收,OpenSSL无法读取有效内容
  • 错误地直接修改out和outlen指针本身,而非通过指针赋值传递数据地址和长度

修正后的完整代码

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "openssl/ssl.h"
#include "openssl/err.h"

SSL_CTX* InitCTX(void)
{
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    OpenSSL_add_all_algorithms();  
    SSL_load_error_strings();   
    method = TLS_client_method();  
    ctx = SSL_CTX_new(method);  
    if (ctx == NULL) {
        ERR_print_errors_fp(stderr);
        abort();
    }
    return ctx;
}

static int ext_add_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg)
{
    printf("in ext_add_cb\n");
    const char *ext_data = "testing";
    size_t data_len = strlen(ext_data);
    
    // 分配堆内存,确保回调结束后数据有效
    unsigned char *data_buf = OPENSSL_malloc(data_len);
    if (!data_buf) {
        *al = SSL_AD_INTERNAL_ERROR;
        return 0;
    }
    memcpy(data_buf, ext_data, data_len);
    
    // 正确传递数据:修改指针指向的内容
    *out = data_buf;
    *outlen = data_len;
    return 1;
}

static void ext_free_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *out, void *add_arg)
{
    printf("in ext_free_cb\n");
    OPENSSL_free((unsigned char *)out);
}

static int ext_parse_cb(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *in, size_t inlen, X509 *x, size_t chainidx, int *al, void *parse_arg)
{
    printf("in ext_parse_cb\n");
    return 1;
}

int main()
{
    struct sockaddr_in servaddr; 
    char *host = "127.0.0.1";
    char *port = "443";
    int sockfd = 0; 
    SSL_CTX * ctx = NULL;
    SSL * ssl = NULL;

    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htons(atoi(port)); 
    servaddr.sin_addr.s_addr = inet_addr(host); 

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("socket error \n");
        return -1;
    }

    if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        printf("connection failed \n");
        return -1;
    }

    SSL_library_init(); 
    OpenSSL_add_all_algorithms();  
    SSL_load_error_strings();  
    ctx = InitCTX();

    int status = SSL_CTX_add_custom_ext(ctx, 65280, SSL_EXT_CLIENT_HELLO, 
                                        ext_add_cb, ext_free_cb, NULL, ext_parse_cb, NULL);
    if (status != 1) {
        printf("SSL_CTX_add_custom_ext() failed\n");
    }

    ssl = SSL_new(ctx);     
    SSL_set_fd(ssl, sockfd);   

    if (SSL_connect(ssl) == -1) {   
        printf("SSL_connect() failed\n");
        ERR_print_errors_fp(stderr);
        return -1;
    }

    char msg[] = "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nAccept: */*\r\n\r\n";
    int bytes = SSL_write(ssl, msg, strlen(msg));

    char buf[1024] = {0};
    bytes = SSL_read(ssl, buf, sizeof(buf) - 1);
    buf[bytes] = '\0';
    printf("Received: %s\n", buf);

    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
    return 0;
}

核心修改点

  • 内存管理:使用OPENSSL_malloc分配堆内存存储扩展数据,避免栈内存回收问题
  • 指针操作:通过*out和*outlen赋值,将数据地址和长度传递给OpenSSL,而非修改指针变量本身
  • 数据长度:移除不必要的字符串结束符,仅传递实际有效数据长度
  • 调试增强:增加SSL_connect失败时的错误打印,便于排查问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:39:55