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: 4)
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
#5
Hello Remy,

I made some adjustments to the project I made again after a long break for SSL support. I will share the final version as summary codes.

Infrastructure Information:
Windows OS: Windows 11 24H2
Unix OS: Ubuntu 24.04.1 LTS
Delphi 12
Standard Indy Component (10.6.2.0) that comes with Delphi

Also, openssl is installed under Linux, but since the old version is not available for Linux, I downloaded the file from the address below.
https://github.com/openssl/openssl/relea....2q.tar.gz
I opened it as tar -xvf openssl-1.0.2q.tar.gz.
cd openssl-1.0.2q
sudo ./config shared
sudo make
sudo make test
I created the libssl.so.1.0.0 and libcrypto.so.1.0.0 files with the commands above.


First of all, I ran it without any problems on the Windows operating system. At the same time, when I checked with wireshark, I saw that the data was transferred encrypted with TLSv1.2.

My question or problem here is when I run the application under Ubuntu Linux.

When SSL is active, I get the following error.


Exception EIdOSSLLoadingRootCertError in module LiftNetServer at 0000000000652051.
Could not load root certificate.
error:02001002Confusedystem library:fopen:No such file or directory.

certs/xxxxxx.xxx
libcrypto.so -> libcrypto.so.1.0.0
libcrypto.so.1.0.0
libssl.so -> libssl.so.1.0.0
libssl.so.1.0.0
myapp
myapp_settings.ini


Where should I place the libssl.so.1.0.0 and libcrypto.so.1.0.0 files?
Where does TCP Server look for these files?
In the same directory as the application?
Or will it look under this directory?
/usr/lib/x86_64-linux-gnu/


Code:
procedure TServer.InitTCPConnection;
begin
  IdTCPServer := TIdTCPServer.Create;

  if FConfig.UseSSL then
  begin
    IdServerIOHandlerSSLOpenSSL := TIdServerIOHandlerSSLOpenSSL.Create;

    // Avoid using SSL
    IdServerIOHandlerSSLOpenSSL.OnGetPassword := SSLIOHandlerSocketOpenSSLGetPassword;
    IdServerIOHandlerSSLOpenSSL.OnGetPasswordEx := SSLIOHandlerSocketOpenSSLGetPasswordEx;
    IdServerIOHandlerSSLOpenSSL.OnStatus := SSLIOHandlerSocketOpenSSLStatus;
    IdServerIOHandlerSSLOpenSSL.OnStatusInfo := SSLIOHandlerSocketOpenSSLStatusInfo;
    IdServerIOHandlerSSLOpenSSL.OnVerifyPeer := SSLIOHandlerSocketOpenSSLVerifyPeer;
    IdServerIOHandlerSSLOpenSSL.OnStatusInfoEx := SSLIOHandlerSocketOpenSSLStatusInfoEx;

    IdServerIOHandlerSSLOpenSSL.SSLOptions.RootCertFile := ExtractFilePath(ParamStr(0)) + 'certs' + PathDelim + 'ca.crt';
    IdServerIOHandlerSSLOpenSSL.SSLOptions.CertFile := ExtractFilePath(ParamStr(0)) + 'certs' + PathDelim + 'server.crt';
    IdServerIOHandlerSSLOpenSSL.SSLOptions.KeyFile := ExtractFilePath(ParamStr(0)) + 'certs' + PathDelim + 'server.key';
    IdServerIOHandlerSSLOpenSSL.SSLOptions.Method := sslvTLSv1_2;
    IdServerIOHandlerSSLOpenSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_2];
    IdServerIOHandlerSSLOpenSSL.SSLOptions.Mode := sslmServer;
    IdServerIOHandlerSSLOpenSSL.SSLOptions.VerifyMode := [];
    IdServerIOHandlerSSLOpenSSL.SSLOptions.VerifyDepth := 0;


    IdTCPServer.IOHandler := IdServerIOHandlerSSLOpenSSL;
  end;


  IdTCPServer.Active := False;
  IdTCPServer.MaxConnections := FConfig.MaxConnection; // connection
  IdTCPServer.ListenQueue := 1000;
  IdTCPServer.TerminateWaitTime := 10000;
//  IdTCPServer.UseNagle := False;
//  IdTCPServer.ReuseSocket := rsTrue;

  IdTCPServer.OnConnect := IdTCPServerConnect;
  IdTCPServer.OnDisconnect := IdTCPServerDisconnect;
  IdTCPServer.OnExecute := IdTCPServerExecute;
  IdTCPServer.OnBeforeBind := IdTCPServerBeforeBind;
  IdTCPServer.OnAfterBind := IdTCPServerAfterBind;
  IdTCPServer.OnBeforeListenerRun := IdTCPServerBeforeListenerRun;
  IdTCPServer.OnException := IdTCPServerException;
  IdTCPServer.OnStatus := IdTCPServerStatus;

  IdTCPServer.DefaultPort := FConfig.CloudPort;
end;
...
...
...
Server.IdTCPServer.Active := True;

When using under Windows, I keep the dll files in the link below in the same directory as the application.

https://github.com/IndySockets/OpenSSL-B...-win32.zip

Everything works fine. I feel like I've gone blind after dealing with this for so long.

Actually, the error says that the ssl certificates could not be loaded. There is no problem with the libraries.

When I checked the certs folder. I saw that the certificates were missing.

When I added the necessary files under certs, it worked fine.

In summary, to guide those who have the same problem.
I downloaded and compiled the old version of the openssl library.
As a result of the compilation, the following files were created under the /openssl-1.0.2q/ directory.

I copied 2 files starting with libcrypto.so* and 2 files starting with libssl.so*, 4 files in total, to the application directory.

The application directory contents are as follows.
---------------------
libcrypto.so
libcrypto.so.1.0.0
libssl.so
libssl.so.1.0.0
myapp
myapp_settings.ini
certs/ca.crt
certs/server.key
certs/server.crt
---------------------


Code:
-rw-r--r--  1 administrator administrator    3709 Nov 20  2018 install.com
-rw-r--r--  1 root          root          4599294 Jan 23 09:45 libcrypto.a
-rw-r--r--  1 root          root              293 Jan 23 09:45 libcrypto.pc
lrwxrwxrwx  2 root          root               18 Jan 23 09:45 libcrypto.so -> libcrypto.so.1.0.0
-rwxr-xr-x  1 root          root          2738664 Jan 23 09:45 libcrypto.so.1.0.0
-rw-r--r--  1 root          root           837322 Jan 23 09:45 libssl.a
-rw-r--r--  1 root          root              294 Jan 23 09:45 libssl.pc
lrwxrwxrwx  2 root          root               15 Jan 23 09:45 libssl.so -> libssl.so.1.0.0
-rwxr-xr-x  1 root          root           537376 Jan 23 09:45 libssl.so.1.0.0
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)