Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
SSL TcpServer, TcpClient
#1
Exclamation I want to make a TCP Socket application that is secured with simple self signed SSL certificates. Exclamation

In fact, IndyTCPServer and IndyTcpClient allow me to do most of the operations very easily.
However, when it comes to SSL operations, I fail.

I really don't understand why I can't do this. There are many examples, blogs and videos. But it shouldn't be this hard.
I have easily done a similar SSL operation for Mosquitto Broker on Linux. Angry

I am writing the operations I have done step by step. Please tell me where I am missing or making a mistake.

  1. I downloaded the openssl-1.0.2u-i386-win32.zip package from the address below.
    https://github.com/IndySockets/OpenSSL-Binaries
  2. I created my self-signed certificates with the following steps.
  3. I have made SSL settings. As seen in the screenshot, I am establishing a connection with the Server.
        Then I get an error in the Connect process with the Client. When I look at it with Debug, the first error that appears is,    "Error accepting connection with SSL."then the error I see on the screen is    "Connection reset by peer."
Code:
## SERVER ##
openssl genpkey -algorithm RSA -out server.key -aes256
  Enter PEM pass phrase: 123456
openssl req -new -key server.key -out server.csr
  Validate pass: 123456
    Country: TR
    State: Blank
    Locality: Blank
    Organization Name: Acme Ltd
    Organization Unit: IT
    Common Name: 127.0.0.1
    Email: Blank
    Extra Attributes: Blank
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
  Validate pass: 123456


## CLIENT ##
openssl genpkey -algorithm RSA -out client.key -aes256
  Enter PEM pass phrase: 123456
openssl req -new -key client.key -out client.csr
  Validate pass: 123456
openssl req -new -key server.key -out server.csr
  Validate pass: 123456
    Country: TR
    State: Blank
    Locality: Blank
    Organization Name: Acme Ltd
    Organization Unit: IT
    Common Name: 127.0.0.1
    Email: Blank
    Extra Attributes: Blank
openssl x509 -req -days 365 -in client.csr -signkey client.key -out client.crt
  Validate pass: 123456

I have attached the project files. If you could please tell me what the problem is, I would be very grateful.


Code:
procedure TfrmMain.IdTCPServer1Connect(AContext: TIdContext);
begin
  if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then
    TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := False;
  mmoSrv.Lines.Add('Connected');
end;

procedure TfrmMain.IdServerIOHandlerSSLOpenSSL1GetPassword(var Password: string);
begin
  Password := '123456';
end;

procedure TfrmMain.IdSSLIOHandlerSocketOpenSSL1GetPassword(var Password: string);
begin
  Password := '123456';
end;

I used the files in step 1 to create the certificate and as dll.


Attached Files
.zip   clienttest.zip (Size: 58.99 KB / Downloads: 2)
Reply
#2
(08-28-2024, 01:50 PM)3ddark Wrote: I have attached the project files. If you could please tell me what the problem is, I would be very grateful.

I can't really answer your certificate question, as I don't use certificates myself when testing SSL/TLS. Have you tried using the SSLIOHandler's OnStatusInfo/Ex events to get more details about the handshake's progress?

But, I do have a few notes on other things in your code, which may or may not be related to your error, but they nonetheless are issues that can cause unwanted side effects:

- In btnConnectSrvClick(), deactivating and then re-activating the TIdTCPServer will not clear its Bindings collection. So, updating only the server's DefaultPort with a new value will not actually listen on the new port unless you clear/update the existing Bindings items.

- In btnSendCliClick(), do not use TIdIOHandler.WriteDirect(), use TIdIOHandler.Write(String) instead. Not that it really matters since your server is not reading any data at all. As such, if the client sends enough messages, it will eventually fill up its outgoing buffer, which will then cause your UI thread to become blocked waiting on the server.

- In your TIdTCPServer and TIdThreadComponent event handlers, accessing UI controls directly is not thread-safe and can lead to undesired symptoms, including crashes. The events are fired in the context of worker threads, so you MUST synchronize with the main UI thread when accessing UI controls. You can use TIdSync/TIdNotify or TThread.Synchronize()/.Queue() for that purpose (which you are already doing in IdThreadComponent1Run(), but not in the other events).

- In IdThreadComponent1Run(), you are assigning the TIdIOHandler.DefStringEncoding, but you are not performing any actions that will actually use it. You are reading an arbitrary number of bytes and converting them as-is to a String using Indy's default encoding, which is ASCII not UTF-8. Not that it matters, since the server is not sending any data to begin with, but even if it did, you are not ensuring the bytes are handled correctly.

With that said, try something more like this:

Code:
procedure TfrmMain.ServerLog(const AStr: String);
begin
  TThread.Queue(nil,
    procedure
    begin
      mmoSrv.Lines.Append(DateTimeToStr(Now) + ': ' + AStr);
    end);
end;

procedure TfrmMain.ClientLog(const AStr: String);
begin
  TThread.Queue(nil,
    procedure
    begin
      mmoCli.Lines.Append(DateTimeToStr(Now) + ': ' + AStr);
    end);
end;

procedure TfrmMain.btnConnectSrvClick(Sender: TObject);
var
  ...
begin
  if IdTCPServer1.Active then
  begin
    ...
  end
  else
  begin
    ...
    IdTCPServer1.Bindings.Clear;
    IdTCPServer1.DefaultPort := StrToIntDef(edtPortSrv.Text, 9000);
    ...
  end;
end;

procedure TfrmMain.btnSendCliClick(Sender: TObject);
var
  LMsg: string;
begin
  LMsg := Trim(edtMsgCli.Text);
  if LMsg <> '' then
    IdTCPClient1.IOHandler.WriteLn(LMsg);
end;

function TfrmMain.IdServerIOHandlerSSLOpenSSL1VerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
  Result := AOk;
  if not Result then
    ServerLog('SSL Verify error');
end;

function TfrmMain.IdServerIOHandlerSSLOpenSSL1StatusInfo(const AMsg: String);
begin
  ServerLog('SSL Status - ' + AMsg);
end;

function TfrmMain.IdServerIOHandlerSSLOpenSSL1StatusInfoEx(ASender : TObject;
  const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT; const AType, AMsg : String);
begin
  ServerLog('SSL Status Ex - ' + AType + ', ' + AMsg);
end;

function TfrmMain.IdSSLIOHandlerSocketOpenSSL1VerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
begin
  Result := AOK;
  if not Result then
    ClientLog('SSL Verify error');
end;

function TfrmMain.IdSSLIOHandlerSocketOpenSSL1StatusInfo(const AMsg: String);
begin
  ClientLog('SSL Status - ' + AMsg);
end;

function TfrmMain.IdSSLIOHandlerSocketOpenSSL1StatusInfoEx(ASender : TObject;
  const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT; const AType, AMsg : String);
begin
  ClientLog('SSL Status Ex - ' + AType + ', ' + AMsg);
end;

procedure TfrmMain.IdTCPClient1Connected(Sender: TObject);
begin
  ClientLog('Connected');
  IdTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
  IdThreadComponent1.Active := True;
end;

procedure TfrmMain.IdTCPClient1Disconnected(Sender: TObject);
begin
  IdThreadComponent1.Active := False;
  ClientLog('Disconnected');
end;

procedure TfrmMain.IdTCPServer1Connect(AContext: TIdContext);
var
  AList: TList;
begin
  if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then
    TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := False;
  AList := IdTCPServer1.Contexts.LockList;
  try
    ServerLog('Connected. Total Connections: ' + AList.Count.ToString);
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
end;

procedure TfrmMain.IdTCPServer1Disconnect(AContext: TIdContext);
var
  AList: TList;
begin
  AList := IdTCPServer1.Contexts.LockList;
  try
    ServerLog('Disconnected. Total Connections: ' + (AList.Count-1).ToString);
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;

procedure TfrmMain.IdTCPServer1Exception(AContext: TIdContext;
  AException: Exception);
begin
  ServerLog('Exception - [' + String(AException.ClassName) + '] ' + AException.Message);
end;

procedure TfrmMain.IdTCPServer1Execute(AContext: TIdContext);
var
  LMsg: String;
begin
  LMsg := AContext.Connection.IOHandler.ReadLn;
  ServerLog('Incoming - ' + LMsg);
  AContext.Connection.IOHandler.WriteLn('Got It!');
end;

procedure TfrmMain.IdThreadComponent1Exception(Sender: TIdThreadComponent; AException: Exception);
begin
  ClientLog('Exception - [' + String(AException.ClassName) + '] ' + AException.Message);
end;

procedure TfrmMain.IdThreadComponent1Run(Sender: TIdThreadComponent);
var
  LMsg: string;
begin
  LMsg := IdTCPClient1.IOHandler.ReadLn;
  ClientLog('Incoming - ' + LMsg);
end;

Reply
#3
I will examine the SSL progress errors step by step as you said with Binding.Clear and SslStatus.

I have not yet passed the Client Connect event

The other errors or suggestions you mentioned such as sending data or logging are not important to me at the first stage.

I did not go into much detail because it was a test application that I wrote for testing purposes. There were other codes that I did not use, I extracted and deleted them.

Thanks Remy

Codes that don't work on my work computer (Delphi 10.3 or 11 I dont remember). After adding the logs you mentioned, it worked fine on my personal computer at home with Delphi 12.

I also compiled and ran the project that I had previously added to the mail. The client connected without any problems. Delphi 12 worked without any problems.

It seems to be due to version differences.

   

Thanks Remmy
Reply
#4
(08-28-2024, 05:08 PM)3ddark Wrote: I have not yet passed the Client Connect event


Your symptom suggests the server (or more likely OpenSSL itself) is closing the connection before the TLS handshake is finished. But there is not enough detail provided to diagnose that.

(08-28-2024, 05:08 PM)3ddark Wrote: The other errors or suggestions you mentioned such as sending data or logging are not important to me at the first stage.

I did not go into much detail because it was a test application that I wrote for testing purposes. There were other codes that I did not use, I extracted and deleted them.

Even tests can be faulty, leading to weird results. That's why I mentioned the issues I saw. Doesn't hurt to do things properly from the very beginning.

Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)