0

在 C++ Builder 10.3.1 中,我使用 Indy TCP 客户端-服务器组件 ( TIdTCPClient& TIdTCPServer) 创建与 OpenSSL 进行加密通信的示例。我正在使用这个示例代码:

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();
}

如果没有 OpenSSL,一切正常,但是在添加组件IdSSLIOHandlerSocketOpenSSL1并将IdServerIOHandlerSSLOpenSSL1它们分配给 TCP 客户端-服务器组件(IOHandler属性)后,我收到错误“无法加载 SSL 库”。在那种情况下,我使用了来自https://indy.fulgan.com/SSL/的 OpenSSL 1.0.2 二进制文件(ssleay32.dll 和 libeay32.dll)。

但是,我设法找到了成功加载的旧 OpenSSL 库。仍然,然后我收到以下错误:

使用 SSL 连接时出错。观察到 EOF 违反了协议。

如何使这项工作?

编辑:在客户端和服务器端设置后PassThrough,我得到:false

错误:14094410:SSL 例程:SSL3_READ_BYTES:sslv3 警报握手失败

编辑:这是我的表格的完整代码和 DFM:

单元1.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;
}
//---------------------------------------------------------------------------

单元1.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

单元1.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
4

2 回答 2

3

添加IdSSLIOHandlerSocketOpenSSL1IdServerIOHandlerSSLOpenSSL1组件并将它们分配给 TCP 客户端-服务器组件(IOHandler 属性)后,我收到错误“无法加载 SSL 库”。

确保您的应用程序文件夹或您在程序启动时使用IdOpenSSLSetLibPath()函数指定的文件夹中有最新的 OpenSSL 1.0.2 DLL(Indy 尚不支持 OpenSSL 1.1.x)。IdSSLOpenSSLHeaders.hpp

如果您仍然遇到错误,您可以使用 Indy 的WhichFailedToLoad()函数IdSSLOpenSSLHeaders.hpp来找出未加载 DLL 的原因 - 要么是因为 DLL 本身无法找到或加载到内存中,要么是因为它们缺少 Indy 使用的必需导出函数。

在那种情况下,我使用了来自https://indy.fulgan.com/SSL/的 OpenSSL 1.0.2 二进制文件(ssleay32.dll 和 libeay32.dll)  。

众所周知,这些 DLL 可以与 Indy 一起正常工作。

然后我收到以下错误:

使用 SSL 连接时出错。观察到 EOF 违反了协议。

该错误意味着服务器在客户端仍在执行 SSL/TLS 握手时关闭了 TCP 连接。例如,如果在服务器端引发异常,就会发生这种情况。默认情况下,TIdTCPServer通过关闭套接字来处理未捕获的异常。

不在服务器端设置TIdSSLIOHandlerSocketOpenSSL::PassThrough属性是一个常见的错误。false它需要手动设置,而TIdTCPServer不是自动设置,以允许用户决定哪些端口应该使用 SSL/TLS。 PassThrough需要true在连接的两端设置为。

在客户端,您可以PassThrough在调用之前Connect()(即,对于隐式 SSL)或之后(即,对于类似 STARTTLS 的命令)进行设置。

在服务器端,您可以PassThroughOnConnect事件中设置(即,用于隐式 SSL)或在OnExecute事件中(即,用于类似 STARTTLS 的命令)。

在你的例子中,试试这个:

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();
}

而且,不用说,确保双方的 IOHandler 配置相似,以使用兼容的 SSL/TLS 协议版本、证书等。

于 2019-04-29T18:05:08.513 回答
0

如果您的程序是 32 位,请确保使用 32 位 OpenSSL DLL (i386-win32)。

于 2019-04-29T08:02:10.857 回答