0

I had this function that worked for HTTP. Now, with the wide spread of HTTPS it didn't work anymore. I was getting an "IOHandler value is not valid" error.

So, I created an SSLIOHandler object as shown below, but now I get a new error:

Exception class EIdOSSLUnderlyingCryptoError with message 'Error connecting with SSL. error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version'

How do I setup the SSLIOHandler object properly?

{ Only suitable for small files because the whole download will be saved to RAM before writing it to disk. }
function DownloadFile(CONST SourceURL, DestFileName: string; OUT ErrorMsg: String): Boolean;
VAR
  Indy: TIDHTTP;
  Stream: TFileStream;
  IDAntiFreeze: TIDAntiFreeze;
  IOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  Indy:= TIDHTTP.Create(NIL);
  IOHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(NIL);
  IDAntiFreeze:= TIDAntiFreeze.Create(NIL);
  Stream:= TFileStream.Create(DestFileName, fmCreate);
  TRY
    IOHandler.Destination := SourceURL;
    IOHandler.Host := SourceURL;
    IOHandler.MaxLineAction := maException;
    //IOHandler.Port := 587;
    //IOHandler.DefaultPort := 0;
    //IOHandler.OnStatusInfo := SSLIOHandlerStatusInfo;

    Indy.ConnectTimeout          := 0;                   { Indy default value: zero }
    Indy.ReadTimeout             := -1;                  { Indy default value: -1 }
    Indy.HandleRedirects         := TRUE;                { http://stackoverflow.com/questions/4549809/indy-idhttp-how-to-handle-page-redirects }
    Indy.AllowCookies            := FALSE;
    Indy.Request.UserAgent       := 'Mozilla/4.0';
    Indy.Request.Connection      := 'Keep-Alive';
    Indy.Request.ProxyConnection := 'Keep-Alive';
    Indy.Request.CacheControl    := 'no-cache';
    Indy.IOHandler:= IOHandler;

    // Indy.OnWork:= HttpWork;

    TRY
      Indy.Get(SourceURL, Stream);
      Result:= TRUE;
    EXCEPT
      On E: Exception DO
        begin
          Result:= FALSE;
          ErrorMsg := E.Message + ' (' + IntToStr(Indy.ResponseCode) + ')';
        end;
    END;
  FINALLY
    FreeAndNil(Stream);
    FreeAndNil(IDAntiFreeze);
    FreeAndNil(IOHandler);
    FreeAndNil(Indy);
  END;
end;

PS: The libeay32.dll and ssleay32.dll are present in the app's folder.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • WinInet seems to be too slow: https://stackoverflow.com/questions/6565567/which-is-the-common-average-buffer-size-for-internetreadfile – Gabriel Feb 10 '22 at 21:06
  • 1
    Hasn't this Q been asked many times before? Like here: https://stackoverflow.com/questions/60578855/ssl-error-1409442e-downloading-file-over-https-with-tidhttp, https://stackoverflow.com/questions/48984539/how-can-we-connect-with-a-website-getting-ssl-error-1409442e, etc. These are the top-two matches on a Google search for "Indy 1409442E". – Andreas Rejbrand Feb 10 '22 at 21:11
  • Note that you'll be leaking memory in case the file cannot be created/overwritten (File already open, Directory does not exist, numerous other possibilities). – HeartWare Feb 11 '22 at 07:21

1 Answers1

3

You are not configuring the TIdSSLIOHandlerSocketOpenSSL's TLS settings in any way. By default, it enables only TLS 1.0 (known issue: https://github.com/IndySockets/Indy/issues/181), but chances are that the website in question requires TLS 1.1+ instead.

Try adding this:

IOHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];

Also, just on a side note, you DO NOT need to configure the IOHandler's Destination, Host, or Port properties at all. TIdHTTP will handle that internally for you.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • It worked. Thanks! Maybe the Indy error message can be changed to something less cryptic, like "unsupported SSL version"? :) – Gabriel Feb 10 '22 at 21:07
  • The previous `"IOHandler value is not valid"` error was being raised before the HTTP request was even sent. `TIdHTTP` was detecting an HTTPS url being requested and the assigned `IOHandler` not being able to handle HTTPS, thus it raised. The new error is specific to OpenSSL, not `TIdHTTP`. The HTTPS request is being sent, but the server is rejecting the TLS session. The text of the error is coming from OpenSSL itself, `TIdHTTP` doesn't know the specific cause to raise a more friendly text. Though, OpenSSL's error text does say `"tlsv1 alert protocol version"`, at least – Remy Lebeau Feb 10 '22 at 21:17