You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

ColdFusion开发Alexa自定义技能:签名验证解密获乱码问题

解决ColdFusion开发Alexa技能时的签名验证乱码问题

嘿,我一眼就看出问题出在哪了——你用ColdFusion的Decrypt()函数来处理Alexa的签名验证,这完全用错工具了!Decrypt()是给对称加密算法(比如AES、DES)用的,而Alexa的签名验证是基于非对称RSA签名的,这俩根本不是一回事,难怪你得到乱码。

先给你理清楚核心逻辑:亚马逊是用自己的私钥对请求内容的SHA-1哈希进行签名,我们要做的不是“解密签名得到哈希”,而是用公钥验证这个签名确实是对应请求内容生成的,以此确认请求来自亚马逊且未被篡改。

下面给你两种可行的解决方案,选你顺手的来:

方案一:用OpenSSL命令行完成验证(延续你当前的工具链)

既然你已经在调用OpenSSL,那直接用它的rsautl命令做签名验证就行,步骤调整如下:

  1. 正确处理签名文件:签名是Base64编码的二进制数据,别转成hex,直接写入二进制文件:
<cfset signatureBytes = ToBinary(signature)>
<cffile action="write" file="D:\web\alexa\cert\keys\signature.bin" output="#signatureBytes#" nameconflict="makeunique" binary="true">
  1. 调用OpenSSL验证签名:用rsautl -verify命令验证签名,并输出原始哈希值:
<cfexecute name="D:\openssl.exe" 
    arguments="rsautl -verify -in D:\web\alexa\cert\keys\signature.bin -inkey D:\web\alexa\cert\keys\pubkey.key -pubin -out D:\web\alexa\cert\keys\verified_hash.bin" 
    variable="local.verifyResult" 
    timeout="120">
</cfexecute>
  1. 对比哈希值:把验证得到的哈希(二进制转hex)和你计算的请求内容哈希对比:
<cffile action="read" file="D:\web\alexa\cert\keys\verified_hash.bin" variable="verifiedHashBytes" binary="true">
<cfset verifiedHash = binaryEncode(verifiedHashBytes, "hex")>
<cfset derivedHash = Hash(toString(getHttpRequestData().content), "SHA-1")>

<cfif verifiedHash eq derivedHash>
    <!--- 签名验证通过 --->
<cfelse>
    <!--- 签名验证失败 --->
</cfif>

方案二:用ColdFusion的Java集成直接验证(更高效,无需外部命令)

ColdFusion跑在JVM上,直接调用Java的Signature类就能完成验证,不用折腾文件和外部命令,代码更简洁:

<!--- 1. 处理PEM格式的公钥,去掉头尾标记和换行 --->
<cfset pemPubKey = Replace(local.cert.OpenSSL_Pubkey, "-----BEGIN PUBLIC KEY-----", "", "ALL")>
<cfset pemPubKey = Replace(pemPubKey, "-----END PUBLIC KEY-----", "", "ALL")>
<cfset pemPubKey = ReReplace(pemPubKey, "[\r\n]", "", "ALL")>

<!--- 2. 把PEM公钥转换成Java的PublicKey对象 --->
<cfset keySpec = CreateObject("java", "java.security.spec.X509EncodedKeySpec").Init(ToBinary(pemPubKey))>
<cfset keyFactory = CreateObject("java", "java.security.KeyFactory").getInstance("RSA")>
<cfset publicKey = keyFactory.generatePublic(keySpec)>

<!--- 3. 初始化签名验证器,用SHA1withRSA算法(Alexa要求的) --->
<cfset signatureObj = CreateObject("java", "java.security.Signature").getInstance("SHA1withRSA")>
<cfset signatureObj.initVerify(publicKey)>

<!--- 4. 传入请求内容的字节数组 --->
<cfset requestContent = getHttpRequestData().content>
<cfset signatureObj.update(requestContent)>

<!--- 5. 验证签名(signature是Alexa请求里的Base64签名字符串) --->
<cfset signatureBytes = ToBinary(signature)>
<cfset isSignatureValid = signatureObj.verify(signatureBytes)>

<!--- 最后判断结果 --->
<cfif isSignatureValid>
    WriteOutput("签名验证通过!")
<cfelse>
    WriteOutput("签名验证失败!")
</cfif>

为什么你之前的方法会失败?

再给你补个知识点,避免以后踩坑:

  • Decrypt()是对称加密的解密函数,它要求密钥和加密时用的密钥完全相同,而RSA的公钥和私钥是成对的,用公钥“解密”私钥签名的内容根本不是正确的用法,得到乱码是必然的。
  • 你把签名转成hex写入文件,相当于修改了原始的二进制签名数据,这也会导致后续验证失败。

内容的提问来源于stack exchange,提问作者user3025605

火山引擎 最新活动