Search code examples
delphiindyindy10

TIdTCPClient start TLS after connected


I'm dealing with a protocol that needs to start TLS after connected and handshaked, something like this:

procedure TForm1.Button1Click(Sender: TObject);
var
  SSL: TIdSSLIOHandlerSocketOpenSSL;
begin
  SSL:= TIdSSLIOHandlerSocketOpenSSL.Create;
  SSL.SSLOptions.Method:= sslvTLSv1_2;
  IdTCPClient1.Connect;
  if IdTCPClient1.Connected then
  begin
    //plain-text operations
    HandShake;
    CheckAnswer;
    //finish plain-text start TLS
    IdTCPClient1.IOHandler:= SSL;
    SendTLSSecureBytes;
  end;
end;

Once I get inside SendTLSSecureBytes procedure, and send something over the connection, I get the exception: "Connection closed gracefully"

Something missing to do this work?


Solution

  • You need to assign the SSLIOHandler object to the TIdTCPClient.IOHandler property before you call TIdTCPClient.Connect(). If you do not assign an IOHandler object, Connect() will create a default TCP-only object, and you can't assign a new IOHandler object once the socket connection has been opened.

    Then, set the SSLIOHandler.PassThrough property to False when you are ready to invoke the TLS handshake:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      SSL: TIdSSLIOHandlerSocketOpenSSL;
    begin
      SSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdTCPClient1);
      SSL.SSLOptions.Method := sslvTLSv1_2;
      IdTCPClient1.Connect; // raises exception if failed
      // do plain-text operations, then...
      SSL.PassThrough := False // do TLS handshake
      SendTLSSecureBytes; // will be encrypted by TLS
    end;
    

    Note, for future reference (not applicable to this situation), if you need the TLS handshake to be performed immediately upon establishing the socket connection, you can set the SSLIOHandler.PassThrough property to False before calling TIdTCPClient.Connect(), and the handshake will be completed before Connect() exits:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      SSL: TIdSSLIOHandlerSocketOpenSSL;
    begin
      SSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdTCPClient1);
      SSL.SSLOptions.Method := sslvTLSv1_2;
      SSL.PassThrough := False // do TLS handshake upon connect
      IdTCPClient1.Connect; // raises exception if failed
      SendTLSSecureBytes; // will be encrypted by TLS
    end;