I'm trying to work through the steps listed here for an Alexa Skill I'm developing in Java.
I'm getting a request from Alexa which is a POST. Two of the headers are the signing certificate chain url and the signature.
Amazon SHA1 hashes then signs the entire body of the Alexa request with an X509 key, and then base64 encodes the signed body. This is the "signature." The signing certificate chain is the url where I can GET the X509 certificate chain that contains their public key.
What I need to do is base64 decode the signature, then use the X509 public key to decrypt the signature. This leaves me with a SHA1 hashed request body. Then I need to SHA1 hash the body of the request myself and compare the two.
I validate the certificate chain. I extract the public key. I hash the body of the POST and produce a derived hash value (its SHA1withRSA). I base64 decode the "signature" then decrypt it with the public key to get the asserted hash value.
I've not been able to produce a derived hash value that matches the asserted hash value. This is where I'm stuck and I can't understand what I'm doing wrong. I don't really understand this encryption stuff very well so perhaps I'm missing something super simple.
Step 8 from the link above is where I'm stuck.
First, I borrowed the code from the alexa SDK here. The problem is this code doesn't seem to work:
Signature signature = Signature.getInstance(ServletConstants.SIGNATURE_ALGORITHM);
signature.initVerify(signingCertificate.getPublicKey());
signature.update(body);
if (!signature.verify(Base64.decodeBase64(baseEncoded64Signature.getBytes(ServletConstants.CHARACTER_ENCODING)))) {
throw new SecurityException("Failed to verify the signature/certificate for the provided skill request");
}
SIGNATURE_ALGORITHM
= SHA1withRSA CHARACTER_ENCODING
= UTF-8. signingCertificate
is the X509Certificate.
This code wasn't giving me matching derived and asserted hash values. So I followed this tutorial which used the Cipher Class.
I created a scratch file and hard coded the values in. I extracted the body from the request. I used postman to get the body of the x509 certificate chain. I also took the signature header from the request.
byte[] decodedSignature = Base64.decodeBase64(encodedSignature);
Cipher cipher = Cipher.getInstance("RSA");
PublicKey key = signingCertificate.getPublicKey();
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedSig = cipher.doFinal(decodedSignature);
byte[] hashedBody = DigestUtils.sha1(body);
body
= converted the body of the alexa request to byte. encodedSignature
= the signature header provided in the alexa request.
Obviously the decryptedSig
and hashedBody
are not matching. See below.
decryptedSig
: 48,33,48,9,6,5,43,14,3,2,26,5,0,4,20,-68,25,70,-54,-63,91,-37,73,34,82,-63,62,45,48,-117,112,42,18,-24,-113
hashedBody
: -107,76,55,24,9,79,77,-21,101,57,-103,25,42,-54,-28,5,34,-26,117,38
The problem is: I have no idea what the problem is. Like I said, I don't understand this stuff. I'm just trying to build an alexa skill and this is part of what Amazon requires.
This is the base64 encoded signature:
fmBSIwM+GIN977W9ztbagtnMalXPJBWat8KwoWBauAIrXHaKvjVlY8hqA/vXEdzPYy7rL0B6Tw9uUeHYah6LU7xISIiUpZjm1Ls2t1Nt2LXbyTgLGdNU4RQJiSxoq+87BEmOOBUNTGiDOveZs/9+KTgQgLgyelG6wHwk34p6w/TgqardQ39vjpzqui63s5/2om1KgJs5e1gt24Cemapr6f+Slz0xmmdLmLZ1Hn7nNgnIB3UjQzcVxU6KYJ1rNfnzZNHFSPcnrZ9ArvUT+M7OM10NfkPp53M6Oy3/5pibV13iQKibFijTCZQEFGLl6fXBgoWpBr1iWyYZbUGTk2+yow==
This is the body of the request:
{"version":"1.0","session":{"new":false,"sessionId":"amzn1.echo-api.session.4ebd9d8c-d76c-403f-b82b-952492fffa74","application":{"applicationId":"amzn1.ask.skill.1ac44f3a-696a-4cc0-9944-6b7d9440b394"},"user":{"userId":"amzn1.ask.account.AGO62C2OKQUIGVD4J6SWHKOERZDPPMYLKHP5GAA67TO6Y6KNOGDGGKFHJE6LEYSQTQQ6GJNGSCDIQUYLMYFQXJPV53YEZPLW4AJPOLH7TCMYDKUMZM2QXBSEDEJ43VRLKFF6WUBB47AW7MRKVDE427DQMYX3KIFKO7ZCDPJKQGANEMSNWLWZRICRGVPM6YBOHPV3BB47PZKGSHI"}},"context":{"Viewports":[{"type":"APL","id":"main","shape":"RECTANGLE","dpi":160,"presentationType":"STANDARD","canRotate":false,"configuration":{"current":{"mode":"HUB","video":{"codecs":["H_264_42","H_264_41"]},"size":{"type":"DISCRETE","pixelWidth":1024,"pixelHeight":600}}}}],"Viewport":{"experiences":[{"arcMinuteWidth":246,"arcMinuteHeight":144,"canRotate":false,"canResize":false}],"mode":"HUB","shape":"RECTANGLE","pixelWidth":1024,"pixelHeight":600,"dpi":160,"currentPixelWidth":1024,"currentPixelHeight":600,"touch":["SINGLE"],"video":{"codecs":["H_264_42","H_264_41"]}},"System":{"application":{"applicationId":"amzn1.ask.skill.1ac44f3a-696a-4cc0-9944-6b7d9440b394"},"user":{"userId":"amzn1.ask.account.AGO62C2OKQUIGVD4J6SWHKOERZDPPMYLKHP5GAA67TO6Y6KNOGDGGKFHJE6LEYSQTQQ6GJNGSCDIQUYLMYFQXJPV53YEZPLW4AJPOLH7TCMYDKUMZM2QXBSEDEJ43VRLKFF6WUBB47AW7MRKVDE427DQMYX3KIFKO7ZCDPJKQGANEMSNWLWZRICRGVPM6YBOHPV3BB47PZKGSHI"},"device":{"deviceId":"amzn1.ask.device.AGLWBJS53GJU5GT755HMYMOH7MCGVSVQAICMZGBZSUNVY2OE6DNQFG4K4UMM3R5NPJR6XSHAABZ44VV6BOUR7SVPZF5DJUXXCTEUAQTCCRZSXKHWWS7N4CAEHGK4VGBHJM57ARCABSPZ4C4LACWJX65ZBKZ5N6LGHZVXIPHJMGQBPCYGGWZIE","supportedInterfaces":{}},"apiEndpoint":"https://api.amazonalexa.com","apiAccessToken":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOiJodHRwczovL2FwaS5hbWF6b25hbGV4YS5jb20iLCJpc3MiOiJBbGV4YVNraWxsS2l0Iiwic3ViIjoiYW16bjEuYXNrLnNraWxsLjFhYzQ0ZjNhLTY5NmEtNGNjMC05OTQ0LTZiN2Q5NDQwYjM5NCIsImV4cCI6MTYxMDE1Mjk2MiwiaWF0IjoxNjEwMTUyNjYyLCJuYmYiOjE2MTAxNTI2NjIsInByaXZhdGVDbGFpbXMiOnsiY29udGV4dCI6IkFBQUFBQUFBQUFBb0FiNFRMdW84M2t4c0FmUEsrYjBpS3dFQUFBQUFBQUM0ekx5ZGFPeGlTdms4UlppLzIrWjduMjFvNEdGYmEveHdqcE5pREo5MzNyb2FyQU5WSnYwUHBBbGwvcWlyK0JhUWQxTjhoY2pkVnhXeTJxQUhrOXNBQmNIRnVJcE5hbTRHYVBKVk9QaTdVZXhic1ZrSEtQSm5Vc0lqSmdtL3JCRm80eGtpWEhNZi84UC9wd0VxMVZIK2xKTHo4ZWRoMGZHUUU3cGQ4TDVYcjcwUlZ0UDFkajEvbld5SGJINVR5RHhHVHU0ZUtGVkJ6ODNKTkRXeG5pa0NHeS9lQjcwNWZBMWxxSDd0QUVRN2lPZUhDR09ZeHh1NE9xcFBvTWgzNHlvUk5wbVZidGd4MGJDQ0dzL3crZHIxRVlIRG50RTdyWnRVM29zei9FOE01UThLV2NCcy8zNERqZlAvaUxvTXVHaWNnOWtRbGNZdm1qWEhpRkNJMDczUEo5NlZweWE3cURVbzc1YmxSZWZuM00wQnJNVVl1VGYzTnN4aElNbFFWQXB2aUhCRndEZVh6eWVtdnc9PSIsImNvbnNlbnRUb2tlbiI6bnVsbCwiZGV2aWNlSWQiOiJhbXpuMS5hc2suZGV2aWNlLkFHTFdCSlM1M0dKVTVHVDc1NUhNWU1PSDdNQ0dWU1ZRQUlDTVpHQlpTVU5WWTJPRTZETlFGRzRLNFVNTTNSNU5QSlI2WFNIQUFCWjQ0VlY2Qk9VUjdTVlBaRjVESlVYWENURVVBUVRDQ1JaU1hLSFdXUzdONENBRUhHSzRWR0JISk01N0FSQ0FCU1BaNEM0TEFDV0pYNjVaQktaNU42TEdIWlZYSVBISk1HUUJQQ1lHR1daSUUiLCJ1c2VySWQiOiJhbXpuMS5hc2suYWNjb3VudC5BR082MkMyT0tRVUlHVkQ0SjZTV0hLT0VSWkRQUE1ZTEtIUDVHQUE2N1RPNlk2S05PR0RHR0tGSEpFNkxFWVNRVFFRNkdKTkdTQ0RJUVVZTE1ZRlFYSlBWNTNZRVpQTFc0QUpQT0xIN1RDTVlES1VNWk0yUVhCU0VERUo0M1ZSTEtGRjZXVUJCNDdBVzdNUktWREU0MjdEUU1ZWDNLSUZLTzdaQ0RQSktRR0FORU1TTldMV1pSSUNSR1ZQTTZZQk9IUFYzQkI0N1BaS0dTSEkifX0.FAaSO9NwDL_lTSST16Fs0Cs-VlYLDpBfD02-m5zYwYvxKDNXcDooRN5SjsLetsNnXT0tyCq20QboCBCqESaDaq9K5RBzkhEQc2BWYp31P9gyEpGIn23YQbm_2JpEDzGwIcZ6CwtXlGyJee7IdZCqDcD9uC7Ytnjf2k-mUAjrTtx4t5XCoy67HhSACh14ySgooW6PRYXKiNrdrOz1VW1dmQKy1obHcAX2fHU7SIEdrQU1Q11-6J2dUH6S2RuMncshhg17GWuzGXGIJW7n-JY5VPEoPSnxXOHnAXZeaCxabVBR9ryaeZUwUxGMF6ZQTBR13L8ea3575os8eBcM6ALtUQ"}},"request":{"type":"SessionEndedRequest","requestId":"amzn1.echo-api.request.d5bd94a6-2011-4f72-b39d-5fcc3c276536","timestamp":"2021-01-09T00:37:42Z","locale":"en-US","reason":"USER_INITIATED"}}
I'm really hoping its something stupid and simple. I appreciate anyone who takes the time to read through this to help me out.