EIdOSSLUnderlyingCryptoError 异常

EIdOSSLUnderlyingCryptoError Exception

我正在使用 Indy(IdHTTP、OpenSSL)。我用这个简单的代码下载了一个页面

var
  IdHTTP: TIdHTTP;
begin
  IdHTTP:=TIdHTTP.Create;
  try
    IdHTTP.Get('https://ezfile.ch/?m=help&a=tos');
  finally
    IdHTTP.Free;
  end;
end;

它returns:

EIdOSSLUnderlyingCryptoError exception "Error connecting with SSL.
    error:14094438:SSL routines:SSL3_READ_BYTES:tlsv1 alert internal error"

该站点使用 TLS 1.1,AES_128_CBC_SHA1,ECDHE-ECDSA。 它应该很容易重现。

尝试了各种 Delphi 版本,Indy 10.6.2,各种 OpenSSL 版本。更改 SSLVersion 选项没有帮助。

可能是什么问题?

此代码适用于 5273 修订版和 OpenSSL 1.0.2 / 1.0.2a:

program HttpsGetExample;

{$APPTYPE CONSOLE}

uses
  IdHTTP, IdGlobal, SysUtils, Classes;

var
  HTTP: TIdHTTP;
  ResponseBody: string;
begin
  HTTP := TIdHTTP.Create;
  try
    try
      ResponseBody := HTTP.Get('https://ezfile.ch');
      WriteLn(ResponseBody);
      WriteLn(HTTP.ResponseText);
    except
      on E: EIdHTTPProtocolException do
      begin
        WriteLn(E.Message);
        WriteLn(E.ErrorMessage);
      end;
      on E: Exception do
      begin
        WriteLn(E.Message);
      end;
    end;
  finally
    HTTP.Free;
  end;
  ReadLn;
  ReportMemoryLeaksOnShutdown := True;
end.

What could be the problem?

该站点对我来说似乎可以运行。我什至可以连接 TLS 1.2。

但是,我在尝试连接 SSLv3 时失败了。这是好事。

这可能是库中的错误。也就是说,它试图与 SSLv3 连接,或者它做了其他错误的事情,比如用 SNI 省略了服务器名称。或者,您在 运行 时加载了错误版本的 OpenSSL。也就是说,您针对 OpenSSL 1.0.2 进行了编译,但是您在 运行 时间加载了一个系统级别较低的版本,例如 0.9.8。

您可以通过获取 CA 然后通过 -CAfile 将其传递给 s_client 来清除下面的 verify error:num=20

$ echo -e "GET /?m=help&a=tos HTTP/1.1\r\nHost: ezfile.ch\r\n\r\n" | \
    openssl s_client -connect ezfile.ch:443 -tls1_1 -servername ezfile.ch -ign_eof
CONNECTED(00000003)
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO ECC Certification Authority
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=PositiveSSL Multi-Domain/CN=sni42046.cloudflaressl.com
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Domain Validation Secure Server CA 2
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Domain Validation Secure Server CA 2
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGtjCCBlugAwIBAgIRAPpZe3LxW3syzgKsuUmSJqEwCgYIKoZIzj0EAwIwgZIx
CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV
BAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYDVQQD
Ey9DT01PRE8gRUNDIERvbWFpbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0Eg
MjAeFw0xNTAzMjUwMDAwMDBaFw0xNTA5MzAyMzU5NTlaMGsxITAfBgNVBAsTGERv
bWFpbiBDb250cm9sIFZhbGlkYXRlZDEhMB8GA1UECxMYUG9zaXRpdmVTU0wgTXVs
dGktRG9tYWluMSMwIQYDVQQDExpzbmk0MjA0Ni5jbG91ZGZsYXJlc3NsLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABGtNNuTz7vtNt80pyh8FgGuPH78FQb1D
FsR0tBKe+ygNLGysmcTkhekQeupBYMFsfHdjUy51iabkf1a2m75iqM6jggS2MIIE
sjAfBgNVHSMEGDAWgBRACWFn8LyDcU/eEggsb9TUK3Y9ljAdBgNVHQ4EFgQUo9+D
XFOnVv7lgCtpOyO+2vbluvswDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCME8GA1UdIARIMEYwOgYLKwYB
BAGyMQECAgcwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNv
bS9DUFMwCAYGZ4EMAQIBMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwuY29t
b2RvY2E0LmNvbS9DT01PRE9FQ0NEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVy
Q0EyLmNybDCBiAYIKwYBBQUHAQEEfDB6MFEGCCsGAQUFBzAChkVodHRwOi8vY3J0
LmNvbW9kb2NhNC5jb20vQ09NT0RPRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNl
cnZlckNBMi5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLmNvbW9kb2NhNC5j
b20wggL9BgNVHREEggL0MIIC8IIac25pNDIwNDYuY2xvdWRmbGFyZXNzbC5jb22C
ECouYm90aXNrYWNhZmUucnOCFCouZGVlZm9yZGVzaWduLmNvLnVrgg0qLmRvcmVn
YW1hLnR2gg8qLmR1dGNocG9ybi5vcmeCCyouZXpmaWxlLmNoggwqLmZhaGFtdS5u
ZXSCDCouZmFoYW11Lm9yZ4IPKi5ncmVlbmhhYml0LnVzgg8qLmp1ZGlrYXJ0dS5j
b22CFCouanVtcGZyb21yb29mLnRvZGF5ggwqLm1vYml1cy54eXqCDioubXV0aHJv
bmUubmV0ghEqLm15YWxwaGFob3N0LmNvbYIOKi5uZXdzYmVkcy5jb22CDyoucGF1
bGRpYXouYXNpYYINKi5wYXVsZGlhei5tZYIVKi5wb2thenktY2hlbWljem5lLnBs
ghMqLnNpeHR5c2l4c291bmQuY29tggsqLnNvYXdyLm9yZ4ISKi5zdGV2aWVjcmlw
cHMuY29tghoqLnN3aW5nc2V0cHJpY2Vjb21wYXJlLmNvbYILKi50dWJ5bG8ucGyC
DmJvdGlza2FjYWZlLnJzghJkZWVmb3JkZXNpZ24uY28udWuCC2RvcmVnYW1hLnR2
gg1kdXRjaHBvcm4ub3JnggllemZpbGUuY2iCCmZhaGFtdS5uZXSCCmZhaGFtdS5v
cmeCDWdyZWVuaGFiaXQudXOCDWp1ZGlrYXJ0dS5jb22CEmp1bXBmcm9tcm9vZi50
b2RheYIKbW9iaXVzLnh5eoIMbXV0aHJvbmUubmV0gg9teWFscGhhaG9zdC5jb22C
DG5ld3NiZWRzLmNvbYINcGF1bGRpYXouYXNpYYILcGF1bGRpYXoubWWCE3Bva2F6
eS1jaGVtaWN6bmUucGyCEXNpeHR5c2l4c291bmQuY29tgglzb2F3ci5vcmeCEHN0
ZXZpZWNyaXBwcy5jb22CGHN3aW5nc2V0cHJpY2Vjb21wYXJlLmNvbYIJdHVieWxv
LnBsMAoGCCqGSM49BAMCA0kAMEYCIQCdDfGZutuJWSUn3ytumOYCbpeRjkuHN5ZN
7L0AganZGQIhANpKjy+PNLTWCOzGCcva3fEbCLzpCmu2PUatE9xBl+Ck
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=PositiveSSL Multi-Domain/CN=sni42046.cloudflaressl.com
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO ECC Domain Validation Secure Server CA 2
---
No client certificate CA names sent
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4166 bytes and written 408 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-ECDSA-AES128-SHA
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.1
    Cipher    : ECDHE-ECDSA-AES128-SHA
    Session-ID: ACB2C0516C9F57EE6AA973463849A53B07CAF99A6A78EE6C12AC0CDF99CC9C50
    Session-ID-ctx: 
    Master-Key: 0DCCE2B3E57E034B271296C716CFBDC4039AE4E6697A8EF560FD7423A9090ACEC3F924D331C2B8FD0FAE5631C9D8219A
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 64800 (seconds)
    TLS session ticket:
    0000 - 03 30 b1 f4 75 9a 14 f7-d5 97 03 b3 4e 4d 5e ab   .0..u.......NM^.
    0010 - d1 15 d5 09 4a 7e 88 8b-d1 ba ed 9d 20 b5 bb f4   ....J~...... ...
    0020 - 33 c0 14 44 b3 d7 1d 78-f5 f0 f5 06 dd 57 cb 58   3..D...x.....W.X
    0030 - 51 6d 0a 18 a7 97 1b d6-36 ea bd ab a3 5a bc 1e   Qm......6....Z..
    0040 - 35 47 31 4b 19 cb c5 94-ac c5 41 f1 65 6a 76 d3   5G1K......A.ejv.
    0050 - 9e b2 45 e1 3c 5d dd 4d-49 6f 2f f2 18 1b 88 45   ..E.<].MIo/....E
    0060 - 9b 9d 50 1e 66 e2 ec c9-e5 87 a1 5a b7 80 d3 60   ..P.f......Z...`
    0070 - 6d fe 3e b6 77 0b c2 ba-f9 f9 12 49 f3 55 72 02   m.>.w......I.Ur.
    0080 - b1 da 2b 4c a6 74 50 df-11 12 c9 6b 1d 2f da a8   ..+L.tP....k./..
    0090 - 4f bc c5 9e ff f1 ff 5d-9a 28 ad e9 4d 43 09 ed   O......].(..MC..
    00a0 - bb 7d d6 1d fc 39 75 1e-e2 6e 2f f4 a6 69 7e 6c   .}...9u..n/..i~l
    00b0 - 97 cd 9c 1a 77 0d 14 c7-61 f8 87 cf 24 52 60 3e   ....w...a...$R`>

    Start Time: 1430097932
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---
HTTP/1.1 200 OK
Server: cloudflare-nginx
Date: Mon, 27 Apr 2015 01:18:45 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=dc202f5845fd4246ec401ee26196a7e831430097524; expires=Tue, 26-Apr-16 01:18:44 GMT; path=/; domain=.ezfile.ch; HttpOnly
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified: Mon, 27 Apr 2015 01:18:45 GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
X-Frame-Options: sameorigin
Access-Control-Allow-Origin: *
CF-RAY: 1dd6b1fac4350874-IAD
...

这是有关如何使用 SSL_set_tlsext_host_name

解决此问题的示例代码

这是通过创建一个继承自 TIdHTTP 的自定义 class,并使用 TIdSSLIOHandlerSocketOpenSSL[=15] 的 OnStatusInfoEx 事件以正确的参数调用 SSL_set_tlsext_host_name 来完成的=]

这个问题大约一个月前开始出现在所有 Cloudflare 启用 SSL 的网站上。

program Project1;

{$APPTYPE CONSOLE}

uses
  System.Classes, IdHTTP, IdSSL, IdSSLOpenSSL, IdSSLOpenSSLHeaders, IdCTypes;

type
  TCustomIdHTTP = class(TIdHTTP)
  public
    constructor Create(AOwner: TComponent);
  private
    procedure OnStatusInfoEx(ASender: TObject; const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT; const AType, AMsg: String);
  end;

{ TCustomIdHTTP }

constructor TCustomIdHTTP.Create(AOwner: TComponent);
begin
  IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  with IOHandler as TIdSSLIOHandlerSocketOpenSSL do begin
    OnStatusInfoEx := Self.OnStatusInfoEx;
    SSLOptions.Method := sslvSSLv23;
    SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1, sslvTLSv1];
  end;
  inherited Create(AOwner);
end;

procedure TCustomIdHTTP.OnStatusInfoEx(ASender: TObject; const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT;
  const AType, AMsg: String);
begin
  SSL_set_tlsext_host_name(AsslSocket, Request.Host);
end;
//////////////////

var
  MyHTTP: TCustomIdHTTP;
begin
  MyHTTP := TCustomIdHTTP.Create(nil);
  // Your normal Indy HTTP code here
  MyHTTP.Free;
end.

上述方法 SSL_set_tlsext_host_name 对我有用,除了我必须在添加的析构函数中释放 IOHandler。否则会引发异常。