Sample code for 30+ languages & platforms
Zig Requires Chilkat v11.0.0+

Verify Signature of Alexa Custom Skill Request

See more HTTP Misc Examples

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

Chilkat Zig Downloads

Zig
const std = @import("std");
const chilkat = @import("chilkat");

pub fn main(init: std.process.Init) !void {
    const alloc = init.arena.allocator();

    // 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:
    const signature = "dSUmPwxc9...aKAf8mpEXg==";
    const cert_chain_url = "https://s3.amazonaws.com/echo.api/echo-api-cert-6-ats.pem";
    const json_body = "{\"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
    const http = try chilkat.Http.init();
    defer http.deinit();
    const sb_pem = try chilkat.StringBuilder.init();
    defer sb_pem.deinit();
    http.quickGetSb(cert_chain_url, sb_pem) catch {
        std.debug.print("{s}\n", .{try http.getLastErrorText(alloc)});
        return;
    };

    const pem = try chilkat.Pem.init();
    defer pem.deinit();
    pem.loadPem(try sb_pem.getAsString(alloc), "passwordNotUsed") catch {
        std.debug.print("{s}\n", .{try pem.getLastErrorText(alloc)});
        return;
    };

    // The 1st certificate should be the signing certificate.
ERROR: Method not in the Zig package: Pem.GetCert
ERROR: Method not in the Zig package: Pem.GetCert
    const cert = pem.ERROR();
    if (!pem.getLastMethodSuccess()) {
        std.debug.print("{s}\n", .{try pem.getLastErrorText(alloc)});
        return;
    }

    // Get the public key from the cert.
    const pub_key = try chilkat.PublicKey.init();
    defer pub_key.deinit();
    cert.getPublicKey(pub_key) catch {};

    // Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.
    const rsa = try chilkat.Rsa.init();
    defer rsa.deinit();
    rsa.usePublicKey(pub_key) catch {
        std.debug.print("{s}\n", .{try cert.getLastErrorText(alloc)});
        return;
    };

    // 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.setEncodingMode("base64");
    if (rsa.verifyStringENC(json_body, "sha1", signature)) {
        std.debug.print("The signature is verified against the JSON body of the request. Yay!\n", .{});
    } else |_| {
        std.debug.print("Sorry, not verified.  Crud!\n", .{});
    }
}