0

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
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Tracer
  • 2,544
  • 2
  • 23
  • 58

2 Answers2

4

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Your modifications work for me only if I set `PassThrough` to `true` on both sides.. If it's set to `false` (like you specified in the example) I get handshake failure error? – Tracer Apr 29 '19 at 20:16
  • 1
    @Tracer Is that supposed to be a question or a statement? I guarantee you that this stuff works fine when used *correctly*, so you must be missing something you haven't described yet. Please edit your question to show the actual error, and your actual component setup on both ends. – Remy Lebeau Apr 30 '19 at 00:19
  • I have put everything in a smallest project possible to demonstrate: [link](https://www.dropbox.com/s/jf4hsit9wzekm8v/Indy.zip?dl=1). Please check it out. Upon running this project and trying to send data I get handshake failure error. But, if I change `PassThrough` to `true` it works fine. So, I am a bit confused should it be set to `true` (like you said in your post) or should it be `false` (like you specified in the example above. – Tracer Apr 30 '19 at 07:36
  • @Tracer when `PassThrough` is true, data is "passed through" the socket as-is without SSL/TLS encryption. When `PassThrough` is false, data is encrypted. Everything I've said is accurate. If you are getting a handshake error, it is because you have a mistake somewhere in your setup. But I can't see your setup, and I'm not going to go download code from another site to hunt it down. StackOverflow is a Q&A site, questions are meant to be *self contained*. Again, please edit your question to post the relevant details directly in it, where they belong – Remy Lebeau Apr 30 '19 at 14:43
  • OK. I understand the `PassThrough` now. Sorry, but I don't know what info do you need from me. I only used default settings in all components. Since you don't want to check the sample I sent I can only thank you for your time & help so far, and will try to search elsewhere. Regards! – Tracer Apr 30 '19 at 15:04
  • I do want to help you, but you have to understand that StackOverflow has certain rules, and hosting content on other sites goes against those rules. I have edited your question to include the information I asked for. That being said, there is not enough information to diagnose your problem. The alert means the handshake failed, but it doesn't explain WHY it failed. You will have to sniff the TCP traffic with a packet sniffer, like Wireshark, and look at the actual handshake to see which stage is actually failing (a handshake involves multiple messages back and forth). – Remy Lebeau Apr 30 '19 at 16:01
  • Sure, thank you. I already tried Wireshark, but it does not even detect any traffic through port 5577. It simply captures nothing.. even when without OpenSSL. Anyway, thanks again. – Tracer Apr 30 '19 at 16:04
0

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

Charles-Henri
  • 339
  • 6
  • 11