title: How to retrieve the TLS root CA certificate using Indy and OpenSSL
body: <p>is it possible to retrieve the TLS root CA certificate from a server using Indy and OpenSSL? I already posted this in the (German) Delphi-Praxis <a href="http://www.delphipraxis.net/190534-indy-openssl-komplette-zertifikatskette-im-client-ermitteln.html" rel="nofollow">http://www.delphipraxis.net/190534-indy-openssl-komplette-zertifikatskette-im-client-ermitteln.html</a>
  but got no answer there.</p>
  
  <p>I need to retrieve the whole certificate chain of the server's certificate in order to display it to the user. I do not want to add a bunch of trusted CAs (and keep the list up to date) as probably most of the servers I will deal with will either have self-signed certificates or enterprise internal CAs.</p>
  
  <p>This is the output of my program when accessing mail.startcom.org. I think in my output there is missing the root CA ("/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority")</p>
  
  <pre><code>*** Got certificate. Depth = 1, Error = 20
    Subject: /C=IL/O=StartCom Ltd./OU=StartCom Certification Authority/CN=StartCom Class 3 OV Server CA
    Issuer: /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
    SHA1 Fingerprint: 5A:F7:D2:E0:80:5E:1C:7B:E5:74:22:F3:5E:63:6B:25:94:47:E5:D7
  
  *** Got certificate. Depth = 0, Error = 20
    Subject: /C=IL/ST=HaDarom/L=Eilat/O=StartCom Ltd. (Start Commercial Limited)/CN=mail.startcom.org
    Issuer: /C=IL/O=StartCom Ltd./OU=StartCom Certification Authority/CN=StartCom Class 3 OV Server CA
    SHA1 Fingerprint: F2:65:56:CD:A7:41:73:D8:FE:B6:85:4F:D8:79:E4:BA:3F:4D:78:C7
  EIdConnClosedGracefully: Connection Closed Gracefully.
  </code></pre>
  
  <p>Th openSSL version I used is 1.0.2j and the indy version is the one bundled with Delphi XE2. Compiling the application with 10.1 berlin (and the indy version bundled with) shows the same behaviour.</p>
  
  <p>Here is the code of my sample application:</p>
  
  <pre><code>unit Unit1;
  
  interface
  
  uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  
    IdSmtp, IdSSLOpenSSL, IdExplicitTLSClientServerBase;
  
  type
    TForm1 = class(TForm)
      btnGetCertChain: TButton;
      Memo1: TMemo;
      chkCancelTlsHandshake: TCheckBox;
      editHost: TEdit;
      editPort: TEdit;
      procedure btnGetCertChainClick(Sender: TObject);
      function TlsVerifyPeer(Certificate: TIdX509;
    AOk: Boolean; ADepth, AError: Integer): Boolean;
      procedure FormCreate(Sender: TObject);
      procedure FormDestroy(Sender: TObject);
    private
      FSMTPClient: TIdSMTP;
      procedure InitTls();
    end;
  
  var
    Form1: TForm1;
  
  implementation
  
  {$R *.dfm}
  
  procedure TForm1.btnGetCertChainClick(Sender: TObject);
  begin
    memo1.Clear();
    Memo1.Lines.Add('Trying to connect to ' + editHost.Text + '.' + editPort.Text);
  
    FSMTPClient.Host := editHost.Text;//'mail.startcom.org';
    FSmtpClient.Port := StrToIntDef(editPort.Text, -1);//25;
    try
      FSmtpClient.Connect();
      FSMTPClient.Authenticate(); // calls StartTLS
    except
      on E: Exception do
      begin
        // when we "cancel" the handshake, there will be an exception...
        Memo1.Lines.Add(E.ClassName + ': ' + E.Message);
      end;
    end;
    FSmtpClient.Disconnect();
  end;
  
  procedure TForm1.FormCreate(Sender: TObject);
  begin
    FSMTPClient := TIdSMTP.Create(nil);
    InitTls();
  end;
  
  procedure TForm1.FormDestroy(Sender: TObject);
  begin
    FreeAndNil(FSMTPClient);
  end;
  
  procedure TForm1.InitTls;
  var
    SslIoHandler: TIdSSLIOHandlerSocketOpenSSL;
  begin
    SslIoHandler := TIdSSLIOHandlerSocketOpenSSL.Create(FSMTPClient);
  
    SslIoHandler.SSLOptions.Method := sslvTLSv1;
    SslIoHandler.SSLOptions.VerifyMode := [sslvrfPeer];
    SslIoHandler.SSLOptions.VerifyDepth := 9; // 9 is default: https://linux.die.net/man/3/ssl_ctx_set_verify_depth
  // SslIoHandler.SSLOptions.RootCertFile ; // don't have one
    SslIoHandler.OnVerifyPeer := TlsVerifyPeer; // Necessary for certificate verification
  
    FSMTPClient.IOHandler := SslIoHandler; // ownership of SslIoHandler is moved
    FSMTPClient.UseTLS := utUseRequireTLS;
  end;
  
  function TForm1.TlsVerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth,
    AError: Integer): Boolean;
  begin
   // store/output certificate info... or just output for demonstrational purpose
   Memo1.Lines.Add('');
    Memo1.Lines.Add('*** Got certificate. Depth = ' + inttostr(ADepth)+', Error = ' + IntToStr(AError));
    Memo1.Lines.Add('  Subject: ' + Certificate.Subject.OneLine);
    Memo1.Lines.Add('  Issuer: ' + Certificate.Issuer.OneLine);
    Memo1.Lines.Add('  SHA1 Fingerprint: ' + Certificate.Fingerprints.SHA1AsString);
  
    result := true;
    if chkCancelTlsHandshake.Checked then
    begin
      // for now we do not want to continue - present certificates to the user first
      result := ADepth &gt; 0; // false for leaf cert; true for (intermediate) CAs
    end;
  end;
  
  end.
  </code></pre>
  
  <p>Am I just missing a call or is this a problem with Indy/OpenSSL?</p>