如何在TLS握手成功后读取服务器发送的NewSessionTicket?
TLS 1.3 客户端读取NewSessionTicket并实现PSK会话恢复的正确方法
核心逻辑说明
TLS 1.3的NewSessionTicket是由OpenSSL在握手完成后自动处理并关联到SSL会话对象中的,不需要通过SSL_read读取(该接口仅用于获取应用层数据)。正确的做法是提取并保存握手后的SSL_SESSION对象,后续启动时加载该对象即可自动触发PSK会话恢复。
步骤1:首次握手后保存SSL_SESSION(获取PSK相关信息)
握手成功后,通过以下方式提取并保存会话:
- 调用
SSL_get1_session(ssl)获取当前会话对象(该接口会增加引用计数,避免会话被提前释放) - 将会话序列化到文件或内存,方便后续复用
示例代码:
// 假设ssl已完成握手且状态正常 SSL_SESSION *session = SSL_get1_session(ssl); if (session != NULL) { // 将会话写入文件保存 FILE *fp = fopen("tls_session.pem", "w"); if (fp != NULL) { PEM_write_SSL_SESSION(fp, session); fclose(fp); } SSL_SESSION_free(session); // 释放引用计数,避免内存泄漏 }
步骤2:加载会话实现PSK恢复
后续启动客户端时,加载保存的会话并关联到新的SSL对象,发起握手时OpenSSL会自动尝试PSK恢复:
示例代码:
// 创建新的SSL对象(ssl初始化流程略) SSL_SESSION *saved_session = NULL; FILE *fp = fopen("tls_session.pem", "r"); if (fp != NULL) { saved_session = PEM_read_SSL_SESSION(fp, NULL, NULL, NULL); fclose(fp); } if (saved_session != NULL) { // 将保存的会话关联到当前SSL对象 if (SSL_set_session(ssl, saved_session) == 1) { printf("已加载会话,将尝试PSK恢复\n"); } SSL_SESSION_free(saved_session); // 释放引用 } // 发起握手 if (SSL_connect(ssl) != 1) { // 处理握手错误 } // 验证是否成功恢复会话 if (SSL_session_reused(ssl)) { printf("PSK会话恢复成功\n"); }
对之前错误方法的解释
SSL_read:仅用于读取应用层数据,NewSessionTicket属于TLS握手层记录,不会通过该接口返回,因此读不到内容是正常的。SSL_SESSION_get0_ticket_appdata:该接口用于获取服务器在NewSessionTicket中附加的自定义应用数据,而非PSK本身。如果服务器未设置该数据,返回空是合理的。SSL_get_psk_identity:仅在PSK恢复会话握手完成后才会返回有效标识,首次完整握手阶段还未生成PSK身份,因此返回空指针符合预期。
注意事项
- 确保使用的OpenSSL版本≥1.1.1(只有该版本及以上支持TLS 1.3)
- TLS 1.3会话票证有有效期限制,服务器可能配置超时时间,过期会话无法用于恢复
- 会话文件包含敏感的PSK信息,需妥善存储,避免泄露
内容的提问来源于stack exchange,提问作者Ido
相关产品推荐
相关产品推荐

