2

I'm implementing a validator for validating incoming requests from Amazon Alexa. I'm on step 5 and 6 which state:

5) base64-decode the Signature header value on the request to obtain the encrypted signature.

6) Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.

I've managed to extract the public key from the PEM-encoded X.509 certificate by doing:

$publicKey = openssl_pkey_get_public($pem);
$keyData = openssl_pkey_get_details($publicKey);

Which returns my public key. I've then attempted to decrypt the signature like so:

openssl_public_decrypt(base64_decode($this->signature), $decryptedSignature, $keyData['key']);

 

Which should return me a sha1 hash of the request body for me to compare with the actual request body, but what I get back from $decryptedSignature doesn't appear to be a sha1 hash. I'm hoping I'm missing something obvious but I can't see it.

To make things a little easier, here's a real life base64_encoded signature header returned from Alexa's test service:

DElCRMK3sXkhmnmu3D2HzVyuLHJ3JkABuBy2LCRX+winUhV6pSC9p1ASKFi9DzESsCyQ74izlFSvi3zECbSbT45bI38JpARJlal81YpWKxz2zTX+y6Qi+We/bFHHpU4gZO7nTTVQDWG4ua6EuWDTt3jL4B+hPOzO1OKix0jHKQldaTd9meyanttZ5QK7WotBeS6xU+Pum/dmiQ+LM39NERUCrCRyeU07PUdQt+L5PI8MehMz5ClHFOTWgyjE/J/b4zrX4weppb/KJhqQVmbw79BWMPuaSwf6BIHyf+4+/NSMmoaJ2WMKKEXf1aV7ac71QFFx9pw4P0BX7DK/hqy98Q==

And here is the public key extracted from https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem:

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnK+zBruRA1TnbgQGxE+b 4XiTTZyDkGwJ6068AGsXQmgt9lVhC8CTTC4wdR5NXosboV6/63worQCNo412csBV jUy3H1/VEs+5Kv+AiAOUuKoBfEU8zAvHCc7GmOKUgNidcDA0MSpx3ZTMSGGbkfaL ikRzne6nFZ6jNOnkqTtGD6SrCIYgLNArScYoPzIcXEypHFrognzrR4Ee0YcefGZy S81Yqev/lli01dAgRvpnAty68rYTmxkNhzUSG6IIbFHIxXJKAETAkGiKJcgZpfG2 1Ok5Dk3yGrESY/ID5OnxvMxiXSnXwht8JD6bd15ui0tPDa85B0jpZLloqQZe26oR owIDAQAB -----END PUBLIC KEY-----

jww
  • 97,681
  • 90
  • 411
  • 885
craig_h
  • 31,871
  • 6
  • 59
  • 68

1 Answers1

2

OK, I've realised my mistake. The decrypted signature is returned in binary, so I need to do: bin2hex($decryptedSignature) in order to get the sha1 hash. Bizarrely, the returned signature hash has 30 extra chars prepended, so the actual Alexa hash comparison needs to be:

public function compareHashes($pem) {

  $publicKey = openssl_pkey_get_public($pem);

  openssl_public_decrypt(base64_decode($this->signature), $decryptedSignature, $publicKey);

  $decryptedSignature = hex2bin($decryptedSignature);

  return sha1($this->responseBody) === substr($decryptedSignature, 30);
}

Anyway, I will open source my Alexa validation class and add a link back here once I've passed Alexa certification.

Edit

I'm through certification now, so here's the class I wrote for the Alexa validation: https://github.com/craigh411/alexa-request-validator

craig_h
  • 31,871
  • 6
  • 59
  • 68
  • Hi, Is your skill passed certification. I'm using this library https://github.com/MayBeTall/Alexa-PHP-Endpoint/ can you help me how to do certificate verification. Plz. Thanks. – Gowri Feb 15 '18 at 06:20
  • @gowri yes, my skill passed certification and I've put the validator for incoming Alexa requests up on my [github page](https://github.com/craigh411/alexa-request-validator) which you are free to use. I actually wrote this because I couldn't find any other correct implementation of the required validation; some only did arbitrary checks, some were spaghetti code (and missed certain aspects) and some didn't correctly validate the signature chain url or hashes. – craig_h Feb 15 '18 at 12:08
  • Thx for reply. One more thing, shall I add that validator in all intents are only in `enters` intent. And I'm returning HTTP status code in all intents like `http_response_code(400)` is this correct. – Gowri Feb 16 '18 at 04:24