Delphi 中的 Indy HTTPS POST 请求使用 TLS 1.0 而不是 TLS 1.2

问题描述

我正在尝试在 Delphi XE6 和 Indy (v10.6.2.0) 中发出 HTTPS Post 请求。

我能够成功调用此 API 并使用 JavaScript 获得响应:

JavaScript code causing API response to output twice

但我需要在 Delphi 应用程序中做同样的事情。

我尝试多次尝试使用 TIdSSLIOHandlerSocketOpenSSL 的各种配置,但出现错误:

无法加载 SSL 库

libeay32.dll 是罪魁祸首。我还去了 Indy 的 GitHub 存储库,但不确定需要下载哪个依赖文件。

下面的代码给了我错误:

无法打开文件“C:\Users..\IndyHTTPSTest\Win32\Debug\dXNlcm5hbWU6cGFzc3dvcmQ=”。系统找不到指定的文件。

要让它像 JavaScript 代码一样在 Delphi XE6 中运行,我缺少什么?

我已将 libeay32.dllssleay32.dll 包含在我的项目文件夹中。

这是我的代码:

unit uMainForm;

interface

uses
  Winapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,Vcl.Controls,Vcl.Forms,Vcl.Dialogs,Vcl.StdCtrls,IdBaseComponent,IdSSLOpenSSL,IdSSLOpenSSLHeaders,System.NetEncoding,IdComponent,IdTCPConnection,IdTCPClient,IdHTTP;

type
  TMainForm = class(TForm)
    IdHTTP1: TIdHTTP;
    GetBtn: TButton;
    ResponseMemo: TMemo;
    procedure GetBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;
  ss: TStringStream;
  s,sLog,sFile : String;
  Base64: TBase64Encoding;
  Txt: TextFile;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  // Assign JSON to TStringStream
  ss := TStringStream.Create('locations= [{"address":"Test1","lat":"52.05429","lng":"4.248618"},{"address":"Test2","lat":"52.076892","lng":"4.26975"},{"address":"Test3","lat":"51.669946","lng":"5.61852"},{"address":"Sint-Oedenrode,The Netherlands","lat":"51.589548","lng":"5.432482"}]',TEncoding.UTF8);
  // Base64 encode username and password string to satisfy API for "Authorization","Basic " + sLog);
  s := 'username:password';
  Base64 := TBase64Encoding.Create(20,'');
  sLog := Base64.Encode(s);
end;

procedure TMainForm.GetBtnClick(Sender: TObject);
var
  responseFromServer,sWhichFail,sVer :string;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(self);
  with LHandler do
    begin
      SSLOptions.Method := sslvSSLv2;
      SSLOptions.Mode := sslmUnassigned;
      SSLOptions.VerifyMode := [];
      SSLOptions.VerifyDepth := 0;
      host := '';
    end;

  IdHTTP1 := TIdHTTP.Create(Self);
  with IdHTTP1 do
    begin
      IOHandler := LHandler;
      AllowCookies := True;
      ProxyParams.BasicAuthentication := False;
      ProxyParams.ProxyPort := 0;
      Request.ContentLength := -1;
      Request.ContentType := 'application/x-www-form-urlencoded';
      Request.ContentRangeEnd := 0;
      Request.ContentRangeStart := 0;
      Request.Accept := 'text/json,*/*';
      Request.BasicAuthentication := True;
      Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';

      HTTPOptions := [hoForceEncodeParams];
    end;

    try
      // Set up the request and send it
        sFile:= IdHTTP1.Post('https://api.routexl.com/tour',sLog);
        ResponseMemo.Lines.Add(Format('Response Code: %d',[IdHTTP1.ResponseCode]));
        ResponseMemo.Lines.Add(Format('Response Text: %s',[IdHTTP1.ResponseText]));
    finally
      sWhichFail:= WhichFailedToLoad();
      ShowMessage(sWhichFail);
      ResponseMemo.Lines.Text:= sWhichFail;
      //sVer := OpenSSLVersion();
      //ShowMessage(sVer);
    end;
end;

end.

更新:我更新了代码,并添加了屏幕截图以帮助调试:

unit uMainForm;

interface

uses
  Winapi.Windows,IdHTTP;

type
  TMainForm = class(TForm)
    IdHTTP1: TIdHTTP;
    GetBtn: TButton;
    ResponseMemo: TMemo;
    procedure GetBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    sl: TStringList;
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  sl := TStringList.Create;
  sl.Add('locations=[{"address":"Test1","lng":"5.432482"}]');
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  sl.Free;
end;

procedure TMainForm.GetBtnClick(Sender: TObject);
var
  LHTTP: TIdHTTP;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
  responseFromServer,sVer :string;
begin
  ResponseMemo.Clear;

  LHTTP := TIdHTTP.Create(nil);
  try
    LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(LHTTP);
    LHandler.SSLOptions.SSLVersions := [sslvTLSv1_2];
    LHandler.SSLOptions.Mode := sslmClient;
    LHandler.SSLOptions.VerifyMode := [];
    LHandler.SSLOptions.VerifyDepth := 0;

    LHTTP.IOHandler := LHandler;
    LHTTP.AllowCookies := True;
    LHTTP.Request.Accept := 'text/json,*/*';
    LHTTP.Request.BasicAuthentication := True;
    LHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
    LHTTP.Request.Username := 'username:';
    LHTTP.Request.Password := 'password';

    LHTTP.HTTPOptions := [hoForceEncodeParams];

    try
      responseFromServer := IdHTTP1.Post('https://api.routexl.com/tour',sl);
      ResponseMemo.Lines.Text := responseFromServer;
    except
      on E: EIdOSSLCouldNotLoadSSLLibrary do
      begin
        sVer := OpenSSLVersion();
        sWhichFail := WhichFailedToLoad();
        ResponseMemo.Lines.Add(sVer);
        ResponseMemo.Lines.Add(sWhichFail);
        ShowMessage('Could not load OpenSSL');
      end;
      on E: EIdHTTPProtocolException do
      begin
        ResponseMemo.Lines.Add(Format('Response Code: %d',[LHTTP.ResponseCode]));
        ResponseMemo.Lines.Add(Format('Response Text: %s',[LHTTP.ResponseText]));
        ResponseMemo.Lines.Add(E.ErrorMessage);
        ShowMessage('HTTP Failure');
      end;
      on E: Exception do
      begin
        ResponseMemo.Lines.Add(E.ClassName);
        ResponseMemo.Lines.Add(E.Message);
        ShowMessage('Unknown Error');
      end;
    end;
  finally
    LHTTP.Free;
  end;
end;

end. 

WireShark 在执行 JavaScript 代码时显示以下内容:

image

WireShark 在执行 Delphi 代码时显示以下内容:

image

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)