【问题标题】:TIdHTTPProxyServer SSL SupportTIdHTTPProxyServer SSL 支持
【发布时间】:2013-07-16 13:16:04
【问题描述】:

我正在 delphi xe2 上使用 indy 10.5.8 开发代理服务器 如您所知 TIdHTTPProxyServer 不支持 ssl navtively 所以我向它添加了一个 ssl io 处理程序,并且 ssl 握手没问题,但在那之后服务器返回错误“套接字错误 #10054 连接由对等方重置。”并断开 ssl 库没问题,我只像这样更改了 onbefor 命令:

if (TIdTCPClient(AContext.OutboundClient).Port = 443) then
begin
  if not (AContext.OutboundClient.IOHandler is TIdSSLIOHandlerSocketOpenSSL) then
    begin
      if Assigned(AContext.OutboundClient.IOHandler) then
        AContext.OutboundClient.IOHandler.Free;
      AContext.OutboundClient.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(AContext.OutboundClient);
    end;
  SSLClient := TIdSSLIOHandlerSocketOpenSSL(AContext.OutboundClient.IOHandler);
  SSLClient.SSLOptions.Method     := sslvSSLv23;
  SSLClient.SSLOptions.Mode       := sslmClient;
  SSLClient.SSLOptions.SSLVersions:= [sslvSSLv2, sslvSSLv23, sslvSSLv3, sslvTLSv1];
  SSLClient.SSLOptions.VerifyMode := [];
  SSLClient.OnStatus              := StausChange;
  SSLClient.OnStatusInfo          := StausChangeex;
  SSLClient.PassThrough           := False;
  AContext.OutboundClient.IOHandler.ReadTimeout := 5000;
end else if AContext.OutboundClient.IOHandler is TIdSSLIOHandlerSocketOpenSSL then
begin
  TIdSSLIOHandlerSocketOpenSSL(AContext.OutboundClient.IOHandler).PassThrough := true;
end;

跟踪日志是:

Resolving hostname accounts.google.com.
Connecting to 173.194.70.84.
SSL status: "before/connect initialization"
SSL status: "before/connect initialization"
SSL status: "SSLv2/v3 write client hello A"
SSL status: "SSLv3 read server hello A"
SSL status: "SSLv3 read server certificate A"
SSL status: "SSLv3 read server key exchange A"
SSL status: "SSLv3 read server done A"
SSL status: "SSLv3 write client key exchange A"
SSL status: "SSLv3 write change cipher spec A"
SSL status: "SSLv3 write finished A"
SSL status: "SSLv3 flush data"
SSL status: "SSLv3 read finished A"
SSL status: "SSL negotiation finished successfully"
SSL status: "SSL negotiation finished successfully"
Cipher: name = ECDHE-RSA-AES128-GCM-SHA256; description = ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD
; bits = 128; version = TLSv1/SSLv3; 
SSL status: "SSL negotiation finished successfully"
Disconnected.
Socket Error # 10054
Connection reset by peer.

【问题讨论】:

  • 日期时间正确吗?

标签: delphi https indy proxy-server


【解决方案1】:

如果客户端使用 HTTP CONNECT 命令,则代理根本不应该使用 SSL 本身或对其进行操作。它唯一的工作就是在客户端和目标服务器之间建立一个原始套接字连接,然后在它们之间来回传递原始数据,仅此而已。对于 SSL,这非常很重要,因为代理必须先建立原始连接,然后客户端直接与目标服务器握手,而不是与代理服务器握手,否则客户端无法验证服务器的证书以确保连接。这就是TIdHTTPProxyServer 默认的行为方式,因此请勿在这种情况下尝试手动激活 SSL!

如果客户端使用的是 HTTP GETPOSTHEAD 命令,则客户端不会直接与目标服务器通信。客户端指定代理要连接的完整 URL,然后TIdHTTPProxyServer 充当客户端的 HTTP 服务器,并充当其自己的目标服务器的 HTTP 客户端。在这种情况下,客户端必须直接与TIdHTTPProxyServer 握手。在这种情况下需要注意的一个问题是,由于指定了完整的 URL,因此 URL 中指定的协议隐含了端口号(HTTP 与 HTTPS)。因此,即使是 HTTPS URL,TIdTCPClient(AContext.OutboundClient).Port 也很有可能通常为 80,除非客户端连接到非标准端口,因为 TIdHTTPProxyServer 目前在连接到目标服务器之前不区分 HTTP 和 HTTPS URL。

您可以使用TIdHTTPProxyServerContext.Command 属性来了解客户端是否正在使用CONNECT 命令。

您必须手动处理端口问题,直到可以修复 TIdHTTProxyServer

尝试类似的方法:

procedure TForm1.IdHTTPProxyServer1Connect(AContext: TIdContext);
begin
  if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then begin
    TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := (AContext.Connection.Socket.Binding.Port <> 443);
  end;
end;

procedure TForm1.IdHTTPProxyServer1HTTPBeforeCommand(AContext: TIdHTTPProxyServerContext);
var
  LURI: TIdURI;
  SSLClient: TIdSSLIOHandlerSocketOpenSSL;
begin
  if TextIsSame(AContext.Command, 'CONNECT') then begin
    Exit; // let TIdHTTPProxyServer do its normal behavior...
  end;

  LURI := TIdURI.Create(AContext.Target);
  try
    if not TextIsSame(LURI.Protocol, 'https') then begin
      Exit; // let TIdHTTPProxyServer do its normal behavior...
    end;

    // Port 80 is TIdHTTPProxyServer's default Port when
    // the requested URL does not explicitly specify a
    // port, so update the correct port if needed...
    if (TIdTCPClient(AContext.OutboundClient).Port = 80) and (LURI.Port = '') then begin
      TIdTCPClient(AContext.OutboundClient).Port := 443;
    end;
  finally
    LURI.Free;
  end;

  // The AContext.OutboundClient does not have a default
  // IOHandler assigned yet at this stage...
  SSLClient := TIdSSLIOHandlerSocketOpenSSL.Create(AContext.OutboundClient);
  SSLClient.SSLOptions.Mode       := sslmClient;
  SSLClient.SSLOptions.SSLVersions:= [sslvSSLv2, sslvSSLv3, sslvTLSv1];
  SSLClient.SSLOptions.VerifyMode := [];
  SSLClient.OnStatus              := StausChange;
  SSLClient.OnStatusInfo          := StausChangeex;
  SSLClient.PassThrough           := False;
  SSLClient.ReadTimeout           := 5000;

  AContext.OutboundClient.IOHandler := SSLClient; 
end;

【讨论】:

  • 感谢您快速而有用的回答,亲爱的雷米。使用您的代码,它会打开 https 站点,但不太稳定,例如在使用 gmail 时,我几乎会超时,而 Socket Error #10060 Socket Error #11001 会增加超时并设置套接字超时对我不起作用。我应该再做点什么吗?
猜你喜欢
  • 2011-06-05
  • 2018-08-23
  • 2011-08-18
  • 1970-01-01
  • 2012-04-08
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多