0

Amazon's documentation on how to verify is posted here: https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-a-web-service.html#checking-the-signature-of-the-request

I'm stuck trying to get this to work:

Once you have determined that the signing certificate is valid, extract the public key from it.

Base64-decode the Signature header value on the request to obtain the encrypted signature.

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

Generate a SHA-1 hash value from the full HTTPS request body to produce the derived hash value

Compare the asserted hash value and derived hash values to ensure that they match.

Here's what I have so far. I have the verification done as far as the subject and valid pem file, the issue is I have no idea how to 'Compare the asserted hash value and derived hash values to ensure that they match.'

<!---get the request sent from amazon---->
 <cfset local.request = deserializeJson(
    toString(getHttpRequestData().content)
  )>      

  <!---collect headers for authentication---->
  <cfif this.isTesting is False>
          <cftry>
            <cfset local.request.headers.SignatureCertChainUrl = getHttpRequestData().headers.SignatureCertChainUrl>
          <cfcatch type="any">
            <cfset local.request.request.type = "SessionEndedRequest"><!---end session if SignatureCertChainUrl not passed--->
          </cfcatch>
          </cftry>

          <cftry>
            <cfset local.request.headers.Signature = getHttpRequestData().headers.Signature>
            <cflog file="Alexa" text="URL Sig: #local.request.headers.Signature#" > 
          <cfcatch type="any">
            <cfset local.request.request.type = "SessionEndedRequest"><!---end session if Signature not passed--->
            <cflog file="Alexa" text="Signature is not defined" > 
          </cfcatch>
          </cftry>



  <cfif isDefined("local.request.headers.SignatureCertChainUrl") and 
            isDefined("local.request.headers.Signature")>

               <cfif find('https://s3.amazonaws.com/echo.api/',local.request.headers.SignatureCertChainUrl)
                        or find('https://s3.amazonaws.com:443/echo.api/',local.request.headers.SignatureCertChainUrl)><!---not ssl so kill it--->



                <cflog file="Alexa" text="URL Chain: #local.request.headers.SignatureCertChainUrl#" > 

                <cfset local.request.headers.SignatureCertChainUrlFormatted = replacenocase(local.request.headers.SignatureCertChainUrl,'echo.api/../echo.api','echo.api')>

            <cfhttp url="#local.request.headers.SignatureCertChainUrlFormatted#" method="get"></cfhttp>

                <cffile action="write"
                        file="C:\inetpub\alexa\cert\echo.pem"
                        output="#cfhttp.FileContent#">


                <!---check date is valid---->
                <cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe" 
                       arguments="x509 -noout -dates -in C:\inetpub\alexa\cert\echo.pem"    
                       variable="local.cert.OpenSSL_dates"
                       timeout="10" >
                </cfexecute>

                <cfset local.cert.datesFormatted = replacenoCase(local.cert.OpenSSL_dates,'notbefore','')>
                <cfset local.cert.datesFormatted = replacenoCase(local.cert.datesFormatted,'notafter','')>
                <cfset local.cert.datesFormatted = replacenoCase(local.cert.datesFormatted,'=','')>
                <cfset local.cert.notBefore = trim(listgetat(local.cert.datesFormatted,1,'='))>
                <cfset local.cert.notAfter = trim(listgetat(local.cert.datesFormatted,2,'='))>

                <cfif datecompare(convertCertTime(local.cert.notBefore),now()) is '-1' and 
                        datecompare(convertCertTime(local.cert.notAfter),now()) is '1'>
                    <!---cert date is valid--->
                <cfelse>
                    <cfset local.request.request.type = "SessionEndedRequest">
                </cfif>

                <!---check 'echo-api.amazon.com' in subject---->
                <cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe" 
                       arguments="x509 -noout -subject -in C:\inetpub\alexa\cert\echo.pem"    
                       variable="local.cert.OpenSSL_Subject"
                       timeout="10" >
                </cfexecute>

                <cfif NOT findNOcase('echo-api.amazon.com',local.cert.OpenSSL_Subject)>
                    <cfset local.request.request.type = "SessionEndedRequest">
                </cfif>

                <!---check 'CN=Symantec Class 3 Secure Server CA' in issuer---->
                <cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe" 
                       arguments="x509 -noout -issuer -in C:\inetpub\alexa\cert\echo.pem"    
                       variable="local.cert.OpenSSL_issuer"
                       timeout="10" >
                </cfexecute>

                <cfif NOT findNOcase('Symantec Class 3 Secure Server CA',local.cert.OpenSSL_issuer)>
                    <cfset local.request.request.type = "SessionEndedRequest">
                </cfif>

                <cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe" 
                           arguments="x509 -noout -pubkey -in C:\inetpub\alexa\cert\echo.pem"    
                           variable="local.cert.OpenSSL_Pubkey"
                           timeout="120" >
                </cfexecute>


                  <cffile action="write"
                        file="C:\inetpub\alexa\keys\#local.request.request.requestId#.key"
                        output="#local.cert.OpenSSL_Pubkey#">  

                        <!---decyrpt header signature---->

                        <!----Base64-decode the Signature header---->
                  <cfset local.cert.encryptedSig = toString(ToBinary(local.request.headers.Signature)) /> 

                   <cffile action="write"
                        file="C:\inetpub\alexa\keys\#local.request.request.requestId#.sig"
                        output="#local.cert.encryptedSig#">

               <cfelse>
                <cfset local.request.request.type = "SessionEndedRequest">
               </cfif>     


          <!----end of header decrypting----->     
      <cfelse>
            <!---if either of these are not defined kill the session---->
            <cfset local.request.request.type = "SessionEndedRequest">
      </cfif>


</cfif>
  • Sounds like decrypting the signature gets you the "alleged" hash. Then generate the "actual" hash of the request body and compare the two. Since you are using openssl, maybe this'll help? https://stackoverflow.com/questions/46390136/issue-decrypting-alexa-request-signature-using-openssl-public-decrypt – SOS Dec 15 '17 at 16:07
  • Did you ever get this figured out? – renhack Jan 12 '18 at 16:36

2 Answers2

-1

Try charsetDecode to utf-8 before Base64 decode

-1

Use this before Base64 decode

<CFSET local.request.headers.Signature = charsetDecode( local.request.headers.Signature, "utf-8" )>
Bugs
  • 4,491
  • 9
  • 32
  • 41
Ed K.
  • 1
  • 1