0
votes

In C++ Builder 10.3.1 I am using Indy TCP client-server components (TIdTCPClient & TIdTCPServer) to create an example of encrypted communication with OpenSSL. I'm using this sample code:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    C1->Connect();
    C1->Socket->Write(4);
    int res = C1->Socket->ReadInt32();
    C1->Disconnect();
    ShowMessage(res);
}

void __fastcall TForm1::S1Execute(TIdContext *AContext)
{
    int x = AContext->Connection->Socket->ReadInt32();
    AContext->Connection->Socket->Write(x * x);
    AContext->Connection->Disconnect();
}

Without OpenSSL everything works fine, but after adding IdSSLIOHandlerSocketOpenSSL1 and IdServerIOHandlerSSLOpenSSL1 components and assigning them to TCP client-server components (IOHandler property) I get the error "Could not load SSL library". In that case I used OpenSSL 1.0.2 binaries (ssleay32.dll and libeay32.dll) from https://indy.fulgan.com/SSL/.

But, I managed to find older OpenSSL libraries that were successfully loaded. Still, then I get the following error:

Error connecting with SSL. EOF was observed that violates the protocol.

How to make this work?

EDIT: After setting PassThrough to false both on client & server side I get:

error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

EDIT: Here is my Form's full code and DFM:

Unit1.cpp

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    IdSSLIOHandlerSocketOpenSSL1->PassThrough = false;
    C1->Connect();
    C1->Socket->Write(5);
    int res = C1->Socket->ReadInt32();
    C1->Disconnect();
    ShowMessage(res);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::S1Execute(TIdContext *AContext)
{
    int x = AContext->Connection->Socket->ReadInt32();
    AContext->Connection->Socket->Write(x * x);
    AContext->Connection->Disconnect();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::S1Connect(TIdContext *AContext)
{
    static_cast<TIdSSLIOHandlerSocketOpenSSL*>(AContext->Connection->Socket)->PassThrough = false;
}
//---------------------------------------------------------------------------

Unit1.h

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdCustomTCPServer.hpp>
#include <IdIOHandler.hpp>
#include <IdIOHandlerSocket.hpp>
#include <IdIOHandlerStack.hpp>
#include <IdServerIOHandler.hpp>
#include <IdSSL.hpp>
#include <IdSSLOpenSSL.hpp>
#include <IdTCPClient.hpp>
#include <IdTCPConnection.hpp>
#include <IdTCPServer.hpp>
#include <IdContext.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TIdTCPClient *C1;
    TIdTCPServer *S1;
    TIdServerIOHandlerSSLOpenSSL *IdServerIOHandlerSSLOpenSSL1;
    TIdSSLIOHandlerSocketOpenSSL *IdSSLIOHandlerSocketOpenSSL1;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall S1Execute(TIdContext *AContext);
    void __fastcall S1Connect(TIdContext *AContext);
private:    // User declarations
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.dfm

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 24
    Top = 208
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object C1: TIdTCPClient
    IOHandler = IdSSLIOHandlerSocketOpenSSL1
    ConnectTimeout = 0
    Host = '127.0.0.1'
    IPVersion = Id_IPv4
    Port = 5577
    ReadTimeout = -1
    Left = 168
    Top = 96
  end
  object S1: TIdTCPServer
    Active = True
    Bindings = <
      item
        IP = '0.0.0.0'
        Port = 5577
      end>
    DefaultPort = 0
    IOHandler = IdServerIOHandlerSSLOpenSSL1
    OnConnect = S1Connect
    OnExecute = S1Execute
    Left = 240
    Top = 96
  end
  object IdServerIOHandlerSSLOpenSSL1: TIdServerIOHandlerSSLOpenSSL
    SSLOptions.Mode = sslmUnassigned
    SSLOptions.VerifyMode = []
    SSLOptions.VerifyDepth = 0
    Left = 464
    Top = 40
  end
  object IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL
    Destination = '127.0.0.1:5577'
    Host = '127.0.0.1'
    MaxLineAction = maException
    Port = 5577
    DefaultPort = 0
    SSLOptions.Mode = sslmUnassigned
    SSLOptions.VerifyMode = []
    SSLOptions.VerifyDepth = 0
    Left = 320
    Top = 184
  end
end
2

2 Answers

3
votes

after adding IdSSLIOHandlerSocketOpenSSL1 and IdServerIOHandlerSSLOpenSSL1 components and assigning them to TCP client-server components (IOHandler property) I get the error "Could not load SSL library".

Make sure that you have up-to-date OpenSSL 1.0.2 DLLs (Indy does not yet support OpenSSL 1.1.x) in your app's folder, or in a folder that you specify with the IdOpenSSLSetLibPath() function in IdSSLOpenSSLHeaders.hpp at program startup.

If you are still having the error, you can use Indy's WhichFailedToLoad() function in IdSSLOpenSSLHeaders.hpp to find out why the DLLs are not loading - either because the DLLs themselves cannot be found or loaded into memory, or because they are missing required exported functions that Indy uses.

In that case I used OpenSSL 1.0.2 binaries (ssleay32.dll and libeay32.dll) from https://indy.fulgan.com/SSL/.

Those DLLs are known to work fine with Indy.

then I get the following error:

Error connecting with SSL. EOF was observed that violates the protocol.

That error means the server closed the TCP connection on its end while the client was still performing the SSL/TLS handshake. That can happen, for instance, if an exception is raised on the server side. By default, TIdTCPServer handles an uncaught exception by closing the socket.

It is a common mistake not to set the TIdSSLIOHandlerSocketOpenSSL::PassThrough property to false on the server side. It needs to be set manually, as TIdTCPServer does not set it automatically, to allow users to decide which port(s) should use SSL/TLS. PassThrough needs to be set to true on both ends of the connecton.

On the client side, you can set PassThrough before calling Connect() (ie, for implicit SSL) or after (ie, for STARTTLS-like commands).

On the server side, you can set PassThrough in the OnConnect event (ie, for implicit SSL) or in the OnExecute event (ie, for STARTTLS-like commands).

In your example, try this:

void __fastcall TForm1::Button1Click(TObject *Sender) 
{
    IdSSLIOHandlerSocketOpenSSL1->PassThrough = false;
    C1->Connect();
    C1->Socket->Write(4);
    int res = C1->Socket->ReadInt32();
    C1->Disconnect();
    ShowMessage(res);
}

void __fastcall TForm1::S1Connect(TIdContext *AContext)
{
    static_cast<TIdSSLIOHandlerSocketOpenSSL*>(AContext->Connection->Socket)->PassThrough = false;
}

void __fastcall TForm1::S1Execute(TIdContext *AContext)
{
    int x = AContext->Connection->Socket->ReadInt32();
    AContext->Connection->Socket->Write(x * x);
    AContext->Connection->Disconnect();
}

And, needless to say, make sure the IOHandlers on both sides are configured similarly to use compatible SSL/TLS protocol versions, certificates, etc.

0
votes

Make sure you use the 32 bits OpenSSL DLLs (i386-win32) if your program is 32 bits.