Search code examples
delphisoap

THTTPRIO no firing OnNeedClientCertificate event


I try to use a certificate from the certificate store on Windows for za SOAP service, but it always ask for a certificate with a displayed dialog.

My code:

  ...
  var RIO := THTTPRIO.Create(nil);
  RIO.HTTPWebNode.HTTP.OnNeedClientCertificate := DoOnClientCertificateNeeded;
  RIO.HTTPWebNode.GetHTTPReqResp.PreemptiveAuthentication := true;

  var svc := wsETurizemPorocanje.GetrnoSoap(false, '', RIO);

  data := TXmlData.Create;
  data.LoadFromXml(xml.XML);
  var errors := svc.oddajPorocilo('UserName', 'Password', data , 1);
  ...
end;

procedure TeTurizem.DoOnClientCertificateNeeded(const Sender: TObject; const ARequest: TURLRequest; const ACertificateList: TCertificateList; var AnIndex: Integer);
begin
  for var idx := 0 to ACertificateList.Count - 1 do begin
    if ACertificateList[idx].SerialNum = AppSettings.Server.Hostel.ETurizemSettings.CertSerial then begin
      if (ACertificateList[idx].Start<=Now) and (ACertificateList[idx].Expiry>Now) then begin
         AnIndex := idx;
         break;
      end else
        raise Exception.Create('(DoOnClientCertificateNeeded) Client Certificate Expired.');
    end;
  end;
end;

I'm going nuts, because I can't find a lot of documentation or examples.

What do I need to provide the SOAP call a certificate from the cert store? Isn't defining the event enough?


Solution

  • You need to define an event handler with the following signature:

    procedure MyDoNeedClientCertificate(const Sender: TObject;
      const ARequest: TURLRequest; const ACertificateList: TCertificateList;
      var AnIndex: Integer);
    
    procedure TForm1.MyDoNeedClientCertificate(const Sender: TObject; const ARequest: TURLRequest; const ACertificateList: TCertificateList;
      var AnIndex: Integer);
    begin
      for var I := 0 to ACertificateList.Count - 1 do
      begin
        if ACertificateList[I].CertName.Contains(edCertName.Text) then
        begin
          if ACertificateList[I].Expiry > now then
          begin
            AnIndex := I;
            Exit;
          end;
        end;
      end;
    end;
    

    Then bind that in the HTTPRIO.HTTPWebNode.OnBeforePost event:

    procedure TForm1.HTTPRIO1HTTPWebNode1BeforePost(const HTTPReqResp: THTTPReqResp; Client: THTTPClient);
    begin
      HTTPReqResp.OnNeedClientCertificate := MyDoNeedClientCertificate;
    end;
    

    This way you bind the eventhandler after it gets overwritten in the SetupHttp procedure.