Delphi REST SOAP Client with WSS Signatures and Encryption

 
This tutorial shows how to make a remote procedure call to a REST service with WS-Security data protection and x509 digital signatures. The given example connects to a REST service, which is written using C# WCF. The service implements a simple algorithm, which converts Celsius to Fahrenheit, and vice versa. Both SOAP requests and responses are digitally signed and encrypted using x509 cryptographic keys. Delphi client utilizes the TclHttpRio component from the Clever Internet Suite library.
 
 
Watch on YouTube
 
procedure TForm1.btnC2FClick(Sender: TObject);
begin
  if (edtCelsius.Text = '') then Exit;

  case cbSecurity.ItemIndex of
    0 : ConvertC2F();
    1 : ConvertC2FSign();
    2 : ConvertC2FSignEncrypt();
  end;
end;

procedure TForm1.btnF2CClick(Sender: TObject);
begin
  if (edtFahrenheit.Text = '') then Exit;

  case cbSecurity.ItemIndex of
    0 : ConvertF2C();
    1 : ConvertF2CSign();
    2 : ConvertF2CSignEncrypt();
  end;
end;
 
After WSDL import, Delphi automatically creates the .pas unit with declared data and service types.
 
...

procedure TForm1.ConvertC2FSign;
var
  service: IRestSoapSign;
  celsius, fahrenheit: RestSoapSign.Temperature2;
begin
  service := clHttpRioSign as IRestSoapSign;

  celsius := nil;
  fahrenheit := nil;
  try
    celsius := RestSoapSign.Temperature2.Create();

    celsius.Value := StrToFloat(edtCelsius.Text);
    celsius.Units := 'C';

    fahrenheit := service.Celsius2Fahrenheit(celsius);

    edtFahrenheit.Text := FloatToStr(fahrenheit.Value);
  finally
    fahrenheit.Free();
    celsius.Free();
  end;
end;

procedure TForm1.ConvertC2FSignEncrypt;
var
  service: IRestSoapSignEncrypt;
  celsius, fahrenheit: RestSoapSignEncrypt.Temperature2;
begin
  service := clHttpRioEncrypt as IRestSoapSignEncrypt;

  celsius := nil;
  fahrenheit := nil;
  try
    celsius := RestSoapSignEncrypt.Temperature2.Create();

    celsius.Value := StrToFloat(edtCelsius.Text);
    celsius.Units := 'C';

    fahrenheit := service.Celsius2Fahrenheit(celsius);

    edtFahrenheit.Text := FloatToStr(fahrenheit.Value);
  finally
    fahrenheit.Free();
    celsius.Free();
  end;
end;

...
 
Add a TclSoapMessage.OnGetEncryptionCertificate event handler for the Request object. You need to provide a public-key service certificate here. All used certificates must be installed in Windows.
This example looks for a signing certificate using its fingerprint parameter.
 
procedure TForm1.RequestGetEncryptionCertificate(Sender: TObject;
  AKeyInfo: TclXmlKeyInfo; var ACertificate: TclCertificate;
  AExtraCerts: TclCertificateList; var AStoreName: string;
  var AStoreLocation: TclCertificateStoreLocation; var Handled: Boolean);
begin
  if (clCertificateStore1.Items.Count = 0) then
  begin
    clCertificateStore1.Open('MY');
  end;
  ACertificate := 
    clCertificateStore1.FindByThumbprint('d47ec78db75e82832a5c7fc91895b55fad1de81e', True);
  AStoreName := 'MY';
  AStoreLocation := slCurrentUser;
  Handled := True;
end;

procedure TForm1.RequestGetSigningCertificate(Sender: TObject;
  AKeyInfo: TclXmlKeyInfo; var ACertificate: TclCertificate;
  AExtraCerts: TclCertificateList; var Handled: Boolean);
begin
  if (clCertificateStore1.Items.Count = 0) then
  begin
    clCertificateStore1.Open('MY');
  end;
  ACertificate := 
    clCertificateStore1.FindByThumbprint('4ea0ea7560685b381f729f7a855451ced26a297d', True);
  Handled := True;
end;
 
The Response object requires certificates, as well. You need to add both the TclSoapMessage.OnGetSigningCertificateEx and the OnGetEncryptionCertificate event handlers. By default, the component looks for a server certificate in the Addressbook folder of the CurrentUser storage.
A handler for the OnGetEncryptionCertificate event of the Response requires your personal private-key certificate.
 
procedure TForm1.ResponseGetEncryptionCertificate(Sender: TObject;
  AKeyInfo: TclXmlKeyInfo; var ACertificate: TclCertificate;
  AExtraCerts: TclCertificateList; var AStoreName: string;
  var AStoreLocation: TclCertificateStoreLocation; var Handled: Boolean);
begin
  AStoreName := 'MY';
  AStoreLocation := slCurrentUser;
  Handled := True;
end;

procedure TForm1.ResponseGetSigningCertificateEx(Sender: TObject;
  AKeyInfo: TclXmlKeyInfo; var ACertificate: TclCertificate;
  AExtraCerts: TclCertificateList; var AStoreName: string;
  var AStoreLocation: TclCertificateStoreLocation; var Handled: Boolean);
begin
  AStoreName := 'MY';
  AStoreLocation := slCurrentUser;
  Handled := True;
end;

Add Feedback