Pascal (Lazarus/Delphi)
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
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.