Search code examples
phpalexa-skills-kitpemphp-openssl

Issue decrypting Alexa request signature using openssl_public_decrypt


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-----


Solution

  • 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