Sample code for 30+ languages & platforms
Pascal (Lazarus/Delphi)

Verify Signature of Alexa Custom Skill Request

See more HTTP Misc Examples

This example verifies the signature of an Alexa Custom Skill Request.

Chilkat Pascal (Lazarus/Delphi) Downloads

Pascal (Lazarus/Delphi)
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.PublicKey,
  Chilkat.Pem,
  Chilkat.StringBuilder,
  Chilkat.Rsa,
  Chilkat.Cert,
  Chilkat.Http;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  success: Boolean;
  signature: string;
  certChainUrl: string;
  jsonBody: string;
  http: THttp;
  sbPem: TStringBuilder;
  pem: TPem;
  cert: TCert;
  pubKey: TPublicKey;
  rsa: TRsa;
  bVerified: Boolean;

begin
  success := False;

  //  This example assumes you have a web service that will receive requests from Alexa.
  //  A sample request sent by Alexa will look like the following:

  //  Connection: Keep-Alive
  //  Content-Length: 2583
  //  Content-Type: application/json; charset=utf-8
  //  Accept: application/json
  //  Accept-Charset: utf-8
  //  Host: your.web.server.com
  //  User-Agent: Apache-HttpClient/4.5.x (Java/1.8.0_172)
  //  Signature: dSUmPwxc9...aKAf8mpEXg==
  //  SignatureCertChainUrl: https://s3.amazonaws.com/echo.api/echo-api-cert-6-ats.pem
  //  
  //  {"version":"1.0","session":{"new":true,"sessionId":"amzn1.echo-api.session.433 ... }}

  //  First, assume we've written code to get the 3 pieces of data we need:
  signature := 'dSUmPwxc9...aKAf8mpEXg==';
  certChainUrl := 'https://s3.amazonaws.com/echo.api/echo-api-cert-6-ats.pem';
  jsonBody := '{"version":"1.0","session":{"new":true,"sessionId":"amzn1.echo-api.session.433 ... }}';

  //  To validate the signature, we do the following:

  //  First, download the PEM-encoded X.509 certificate chain that Alexa used to sign the message 
  http := THttp.Create;
  sbPem := TStringBuilder.Create;
  success := http.QuickGetSb(certChainUrl,sbPem);
  if (success = False) then
    begin
      WriteLn(http.LastErrorText);
      Exit;
    end;

  pem := TPem.Create;
  success := pem.LoadPem(sbPem.GetAsString(),'passwordNotUsed');
  if (success = False) then
    begin
      WriteLn(pem.LastErrorText);
      Exit;
    end;

  //  The 1st certificate should be the signing certificate.
  cert := pem.GetCert(0);
  if (pem.LastMethodSuccess = False) then
    begin
      WriteLn(pem.LastErrorText);
      Exit;
    end;

  //  Get the public key from the cert.
  pubKey := TPublicKey.Create;
  cert.GetPublicKey(pubKey);

  cert.Free;

  //  Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.
  rsa := TRsa.Create;
  success := rsa.UsePublicKey(pubKey);
  if (success = False) then
    begin
      WriteLn(cert.LastErrorText);
      Exit;
    end;

  //  RSA "decrypt" the signature.
  //  (Amazon's documentation is confusing, because we're simply verifiying the signature against the SHA-1 hash
  //  of the request body.  This happens in a single call to VerifyStringENC...)
  rsa.EncodingMode := 'base64';
  bVerified := rsa.VerifyStringENC(jsonBody,'sha1',signature);
  if (bVerified = True) then
    begin
      WriteLn('The signature is verified against the JSON body of the request. Yay!');
    end
  else
    begin
      WriteLn('Sorry, not verified.  Crud!');
    end;


  http.Free;
  sbPem.Free;
  pem.Free;
  pubKey.Free;
  rsa.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.