Delphi中TIdCmdTcpServer连接后启用Start TLS的服务器端配置问题
解决Indy SSL动态加密连接问题(基于证书存在性)
服务器端修改步骤
1. 预先检测SSL证书有效性
在服务器启动时,检查证书文件是否存在且可正常加载,标记当前服务器是否支持SSL加密:
private FHasValidSSL: Boolean; ... procedure TIndyTcpCommandReceiver.FormCreate(Sender: TObject); begin FHasValidSSL := False; // 替换为你的证书路径配置 if FileExists(IdServerIOHandlerSSLOpenSSL1.SSLOptions.CertFile) and FileExists(IdServerIOHandlerSSLOpenSSL1.SSLOptions.KeyFile) then begin try // 尝试加载证书验证有效性 IdServerIOHandlerSSLOpenSSL1.SSLOptions.CertFile := IdServerIOHandlerSSLOpenSSL1.SSLOptions.CertFile; IdServerIOHandlerSSLOpenSSL1.SSLOptions.KeyFile := IdServerIOHandlerSSLOpenSSL1.SSLOptions.KeyFile; FHasValidSSL := True; except FHasValidSSL := False; end; end; end;
2. 移除原连接事件中的强制SSL代码
彻底删除TCPServerConnect事件里设置PassThrough := false的代码,让初始连接保持明文状态:
// 原代码全部移除,不要保留 // procedure TIndyTcpCommandReceiver.TCPServerConnect(AContext: TIdContext); // begin // if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketOpenSSL then // TIdSSLIOHandlerSocketOpenSSL(AContext.Connection.IOHandler).PassThrough := false; // end;
3. 添加SSL协商命令(推荐方案)
利用TIdCmdTCPServer的命令机制,新增START_SSL命令让客户端主动询问并触发加密切换:
- 在
TIdCmdTCPServer的Commands集合中添加一个命令,Command属性设为START_SSL,ReplyNormal设为200 SSL enabled。 - 实现命令处理函数:
procedure TIndyTcpCommandReceiver.IdCmdTCPServer1START_SSLCommand(ASender: TIdCommand); var SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL; begin if not FHasValidSSL then begin ASender.Reply.SetReply(500, 'SSL not available on this server'); Exit; end; if ASender.Context.Connection.IOHandler is TIdSSLIOHandlerSocketOpenSSL then begin SSLIOHandler := TIdSSLIOHandlerSocketOpenSSL(ASender.Context.Connection.IOHandler); SSLIOHandler.PassThrough := False; ASender.Reply.SetReply(200, 'SSL enabled'); end else begin ASender.Reply.SetReply(500, 'SSL not supported by server'); end; end;
客户端对应调整
修改客户端连接逻辑,先建立明文连接,再发送命令协商SSL:
procedure TTCPConnector.Connect; var Response: string; begin FSSLIOHandler.PassThrough := true; FTCPClient.Connect(FServerName, FPort); // 发送SSL协商命令 FTCPClient.IOHandler.WriteLn('START_SSL'); Response := FTCPClient.IOHandler.ReadLn; // 根据服务器响应决定是否切换加密模式 if StartsStr('200', Response) then begin FSSLIOHandler.PassThrough := false; // 后续通信已加密 end else begin // 保持明文通信 end; end;
替代方案(无需命令协商)
如果不想修改客户端命令逻辑,可在服务器OnExecute事件中检测SSL握手请求并动态切换:
procedure TIndyTcpCommandReceiver.IdCmdTCPServer1Execute(AContext: TIdContext); var SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL; Buffer: TIdBytes; BytesRead: Integer; begin if not FHasValidSSL then Exit; SSLIOHandler := TIdSSLIOHandlerSocketOpenSSL(AContext.Connection.IOHandler); if SSLIOHandler.PassThrough then begin // 检测SSL Client Hello的起始字节(0x16) BytesRead := SSLIOHandler.CheckForDataOnSource(100); if BytesRead > 0 then begin SSLIOHandler.ReadBytes(Buffer, 1, False); if Buffer[0] = $16 then begin SSLIOHandler.PassThrough := False; // 将读取的字节放回输入缓冲区,让SSL握手正常处理 SSLIOHandler.InputBuffer.Add(Buffer); end; end; end; end;
内容的提问来源于stack exchange,提问作者christian
相关产品推荐
相关产品推荐

