SSL TcpServer, TcpClient - 3ddark - 08-28-2024
I want to make a TCP Socket application that is secured with simple self signed SSL certificates. 
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.
I am writing the operations I have done step by step. Please tell me where I am missing or making a mistake.
- I downloaded the openssl-1.0.2u-i386-win32.zip package from the address below.
https://github.com/IndySockets/OpenSSL-Binaries
- I created my self-signed certificates with the following steps.
- 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.
RE: SSL TcpServer, TcpClient - rlebeau - 08-28-2024
(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;
RE: SSL TcpServer, TcpClient - 3ddark - 08-28-2024
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
RE: SSL TcpServer, TcpClient - rlebeau - 08-28-2024
(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.
RE: SSL TcpServer, TcpClient - 3ddark - 01-23-2025
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/releases/download/OpenSSL_1_0_2q/openssl-1.0.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:02001002 ystem 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-Binaries/blob/master/openssl-1.0.2q-i386-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
|