能否将已建立的SSL连接迁移至另一台服务器?
TLS会话跨机器迁移(基于TCP_REPAIR)的可行方案指导
问题复盘
你的场景是通过TCP_REPAIR迁移长连接,普通TCP已成功,但TLS会话迁移遇阻——两种尝试方案均未实现会话复用,核心问题在于未正确复用TLS的加密上下文状态。
现有方案的核心问题
方案1:SSL_SESSION静默重建失败
你尝试的内存BIO握手流程本质上是重新发起了一次完整握手,生成了新的会话状态,后续替换BIO无法关联原会话的加密密钥。SSL_session_reused()返回0,说明服务器端未识别到可复用的会话上下文。
方案2:memcpy内部结构体不可靠
OpenSSL的内部结构体(如SSL_CONNECTION)属于私有实现,不同版本结构差异极大,硬拷贝无法覆盖所有关键状态(如密钥交换后的派生密钥、读写状态机标记),兼容性和稳定性无法保障。
正确实现步骤(基于OpenSSL公开API)
前置要求
- 新旧服务器必须使用完全相同版本的OpenSSL
- 新旧服务器的
SSL_CTX配置完全一致:加密套件、证书链、会话缓存模式、超时时间等 - 迁移时无TLS消息传输:源机器的SSL对象无
SSL_read()/SSL_write()阻塞,所有缓冲区数据已收发完成
步骤1:源机器导出TLS关键状态
- 导出SSL_SESSION:
SSL_SESSION *sess = SSL_get_session(ssl); unsigned char *buf = NULL; int len = i2d_SSL_SESSION(sess, &buf); // 将buf和len保存/传输到目标机器 - 导出密钥材料与状态标记:
- 记录当前加密套件:
SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); - 导出会话密钥材料:使用
SSL_export_keying_material(),指定相同的上下文标签(如固定字符串"tls-migration"),导出主密钥派生的会话密钥 - 记录SSL状态机标记:
int state = SSL_get_state(ssl);、int rstate = SSL_get_rstate(ssl);
- 记录当前加密套件:
步骤2:目标机器恢复TLS会话
- 恢复TCP连接:通过TCP_REPAIR完成套接字状态恢复,确保TCP序列号、窗口等参数与源机器完全一致
- 创建匹配的SSL对象:
SSL *new_ssl = SSL_new(ctx); // ctx与源机器配置完全一致 SSL_set_fd(new_ssl, repaired_fd); // 绑定恢复后的TCP套接字 - 强制复用SSL_SESSION:
// 从传输的buf中导入SSL_SESSION const unsigned char *p = buf; SSL_SESSION *imported_sess = d2i_SSL_SESSION(NULL, &p, len); SSL_set_session(new_ssl, imported_sess); // 关键:强制标记会话复用,避免触发完整握手 SSL_set_reused_session(new_ssl, imported_sess); - 恢复加密上下文:
- 使用
SSL_export_keying_material(),用相同的上下文标签派生与源机器一致的密钥材料 - 若OpenSSL版本支持,可通过
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)确保会话直接复用
- 使用
- 验证复用状态:
if (SSL_session_reused(new_ssl)) { // 会话复用成功,可正常进行SSL_read/SSL_write }
关键注意事项
- 禁止依赖OpenSSL私有API:优先使用公开API操作,若必须访问内部状态,需针对特定版本做适配(如OpenSSL 1.1.1与3.0的会话结构差异极大)
- 会话超时检查:迁移前确认
SSL_SESSION未过期(SSL_SESSION_get_time()+SSL_SESSION_get_timeout()< 当前时间) - 迁移原子性:源机器导出状态后,立即停止对该SSL对象的所有操作,避免状态不一致
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

