如何使用 x509 公钥来验证签名/散列的 Alexa 请求正文?

如何解决如何使用 x509 公钥来验证签名/散列的 Alexa 请求正文?

我正在尝试针对我正在用 Java 开发的 Alexa 技能执行 here 列出的步骤。

我收到了来自 Alexa 的 POST 请求。其中两个标头是签名证书链 url 和签名。

Amazon SHA1 哈希然后使用 X509 密钥对 Alexa 请求的整个主体进行签名,然后对签名主体进行 base64 编码。这就是“签名”。签名证书链是我可以获取包含其公钥的 X509 证书链的 URL。

我需要做的是对签名进行base64解码,然后使用X509公钥对签名进行解密。这给我留下了一个 SHA1 散列请求正文。然后我需要自己对请求的正文进行 SHA1 散列并比较两者。

我验证了证书链。我提取公钥。我对 POST 的正文进行散列并生成派生的散列值(其 SHA1withRSA)。我对“签名”进行 base64 解码,然后使用公钥对其进行解密以获取断言的哈希值。

我无法生成与断言的哈希值匹配的派生哈希值。这是我被卡住的地方,我无法理解我做错了什么。我不太了解这些加密的东西,所以也许我错过了一些非常简单的东西。

上面链接中的第 8 步是我卡住的地方。

首先,我借用了 alexa SDK here 的代码。问题是这段代码似乎不起作用:


    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 是 X509 证书。

这段代码没有给我匹配的派生和断言的哈希值。所以我遵循了这个使用密码类的 tutorial

我创建了一个临时文件并硬编码了其中的值。我从请求中提取了正文。我使用邮递员来获取 x509 证书链的主体。我还从请求中获取了签名标头。


    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 = 将 alexa 请求的正文转换为字节。
encodedSignature = alexa 请求中提供的签名标头。

显然 decryptedSighashedBody 不匹配。见下文。

decryptedSig: 48,33,48,9,6,5,43,14,3,2,26,4,20,-68,25,70,-54,- 63,91,-37,73,34,82,-63,62,45,-117,112,42,18,-24,-113

hashedBody:-107,76,55,24,79,77,-21,101,57,-103,-28,-26,117,38

问题是:我不知道问题是什么。就像我说的,我不明白这些东西。我只是想建立一项 Alexa 技能,这是亚马逊requires.

这是base64编码的签名:
fmBSIwM + GIN977W9ztbagtnMalXPJBWat8KwoWBauAIrXHaKvjVlY8hqA / vXEdzPYy7rL0B6Tw9uUeHYah6LU7xISIiUpZjm1Ls2t1Nt2LXbyTgLGdNU4RQJiSxoq + 87BEmOOBUNTGiDOveZs / 9 + KTgQgLgyelG6wHwk34p6w / TgqardQ39vjpzqui63s5 / 2om1KgJs5e1gt24Cemapr6f + Slz0xmmdLmLZ1Hn7nNgnIB3UjQzcVxU6KYJ1rNfnzZNHFSPcnrZ9ArvUT + M7OM10NfkPp53M6Oy3 / 5pibV13iQKibFijTCZQEFGLl6fXBgoWpBr1iWyYZbUGTk2 +佑==

这是请求的正文:
{"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 “},” 用户 “:{” 用户id “:” amzn1.ask.account.AGO62C2OKQUIGVD4J6SWHKOERZDPPMYLKHP5GAA67TO6Y6KNOGDGGKFHJE6LEYSQTQQ6GJNGSCDIQUYLMYFQXJPV53YEZPLW4AJPOLH7TCMYDKUMZM2QXBSEDEJ43VRLKFF6WUBB47AW7MRKVDE427DQMYX3KIFKO7ZCDPJKQGANEMSNWLWZRICRGVPM6YBOHPV3BB47PZKGSHI “}},” 上下文 “:{” 视口":[{"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,"canResize":false }],"mode":"HUB","pixelWidth":1024,"pixelHeight":600,"currentPixelWidth":1024,"currentPixelHeight":600," touch":["SINGLE"],"video":{"codecs ":["H_264_42","H_264_41"]}},"系统":{"application":{"applicationId":"amzn1.ask.skill.1ac44f3a-696a-4cc0-9944-6b7d9440b394"},"用户" :{ “用户id”: “amzn1.ask.account.AGO62C2OKQUIGVD4J6SWHKOERZDPPMYLKHP5GAA67TO6Y6KNOGDGGKFHJE6LEYSQTQQ6GJNGSCDIQUYLMYFQXJPV53YEZPLW4AJPOLH7TCMYDKUMZM2QXBSEDEJ43VRLKFF6WUBB47AW7MRKVDE427DQMYX3KIFKO7ZCDPJKQGANEMSNWLWZRICRGVPM6YBOHPV3BB47PZKGSHI”}, “设备”:{ “设备ID”: “amzn1.ask.device.AGLWBJS53GJU5GT755HMYMOH7MCGVSVQAICMZGBZSUNVY2OE6DNQFG4K4UMM3R5NPJR6XSHAABZ44VV6BOUR7SVPZF5DJUXXCTEUAQTCCRZSXKHWWS7N4CAEHGK4VGBHJM57ARCABSPZ4C4LACWJX65ZBKZ5N6LGHZVXIPHJMGQBPCYGGWZIE”, “supportedInterfaces”:{}}, “apiEndpoint”:“HTTPS ://api.amazonalexa.com “ ”apiAccessToken“:” eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOiJodHRwczovL2FwaS5hbWF6b25hbGV4YS5jb20iLCJpc3MiOiJBbGV4YVNraWxsS2l0Iiwic3ViIjoiYW16bjEuYXNrLnNraWxsLjFhYzQ0ZjNhLTY5NmEtNGNjMC05OTQ0LTZiN2Q5NDQwYjM5NCIsImV4cCI6MTYxMDE1Mjk2MiwiaWF0IjoxNjEwMTUyNjYyLCJuYmYiOjE2MTAxNTI2NjIsInByaXZhdGVDbGFpbXMiOnsiY29udGV4dCI6IkFB QUFBQUFBQUFBb0FiNFRMdW84M2t4c0FmUEsrYjBpS3dFQUFBQUFBQUM0ekx5ZGFPeGlTdms4UlppLzIrWjduMjFvNEdGYmEveHdqcE5pREo5MzNyb2FyQU5WSnYwUHBBbGwvcWlyK0JhUWQxTjhoY2pkVnhXeTJxQUhrOXNBQmNIRnVJcE5hbTRHYVBKVk9QaTdVZXhic1ZrSEtQSm5Vc0lqSmdtL3JCRm80eGtpWEhNZi84UC9wd0VxMVZIK2xKTHo4ZWRoMGZHUUU3cGQ4TDVYcjcwUlZ0UDFkajEvbld5SGJINVR5RHhHVHU0ZUtGVkJ6ODNKTkRXeG5pa0NHeS9lQjcwNWZBMWxxSDd0QUVRN2lPZUhDR09ZeHh1NE9xcFBvTWgzNHlvUk5wbVZidGd4MGJDQ0dzL3crZHIxRVlIRG50RTdyWnRVM29zei9FOE01UThLV2NCcy8zNERqZlAvaUxvTXVHaWNnOWtRbGNZdm1qWEhpRkNJMDczUEo5NlZweWE3cURVbzc1YmxSZWZuM00wQnJNVVl1VGYzTnN4aElNbFFWQXB2aUhCRndEZVh6eWVtdnc9PSIsImNvbnNlbnRUb2tlbiI6bnVsbCwiZGV2aWNlSWQiOiJhbXpuMS5hc2suZGV2aWNlLkFHTFdCSlM1M0dKVTVHVDc1NUhNWU1PSDdNQ0dWU1ZRQUlDTVpHQlpTVU5WWTJPRTZETlFGRzRLNFVNTTNSNU5QSlI2WFNIQUFCWjQ0VlY2Qk9VUjdTVlBaRjVESlVYWENURVVBUVRDQ1JaU1hLSFdXUzdONENBRUhHSzRWR0JISk01N0FSQ0FCU1BaNEM0TEFDV0pYNjVaQktaNU42TEdIWlZYSVBISk1HUUJQQ1lHR1daSUUiLCJ1c2VySWQiOiJhbXpuMS5hc2suYWNjb3VudC5BR082MkMyT0tRVUlHVkQ0SjZTV0hLT0VSWkRQUE1ZTEtIUDVHQUE2N1RPNlk2 S05PR0RHR0tGSEpFNkxFWVNRVFFRNkdKTkdTQ0RJUVVZTE1ZRlFYSlBWNTNZRVpQTFc0QUpQT0xIN1RDTVlES1VNWk0yUVhCU0VERUo0M1ZSTEtGRjZXVUJCNDdBVzdNUktWREU0MjdEUU1ZWDNLSUZLTzdaQ0RQSktRR0FORU1TTldMV1pSSUNSR1ZQTTZZQk9IUFYzQkI0N1BaS0dTSEkifX0.FAaSO9NwDL_lTSST16Fs0Cs-VlYLDpBfD02-m5zYwYvxKDNXcDooRN5SjsLetsNnXT0tyCq20QboCBCqESaDaq9K5RBzkhEQc2BWYp31P9gyEpGIn23YQbm_2JpEDzGwIcZ6CwtXlGyJee7IdZCqDcD9uC7Ytnjf2k-mUAjrTtx4t5XCoy67HhSACh14ySgooW6PRYXKiNrdrOz1VW1dmQKy1obHcAX2fHU7SIEdrQU1Q11-6J2dUH6S2RuMncshhg17GWuzGXGIJW7n-JY5VPEoPSnxXOHnAXZeaCxabVBR9ryaeZUwUxGMF6ZQTBR13L8ea3575os8eBcM6ALtUQ “}},” 请求 “:{” 类型 “:” SessionEndedRequest “ ”的requestId“:” amzn1.echo-api.request.d5bd94a6-2011-4f72-b39d -5fcc3c276536","timestamp":"2021-01-09T00:37:42Z","locale":"en-US","re​​ason":"USER_INITIATED"}}

我真的希望它是愚蠢而简单的。感谢所有花时间通读本文以帮助我的人。

解决方法

首先,数字签名不是用私钥加密;亚马逊在那里欺骗你,请参阅 https://security.stackexchange.com/questions/159282/can-openssl-decrypt-the-encrypted-signature-in-an-amazon-alexa-request-to-a-web 基本上是相同的问题,除了没有 Java。而 Java 加密则加剧了这种情况,因为它是在 1990 年代设计的,当时这个错误仍然相当普遍,因此 打算用于加密和解密的 Cipher 对象接受 '反向使用 RSA 密钥,并在内部将它们更改为 Signature 方案“NoneWithRSA”中使用的操作(这可能被视为伪方案,因为它并不真正匹配 PKCS1) .

在这一点上展开,您的“解密”(更准确地说,恢复)值与简单哈希值之间的区别在于此处使用的 PKCS1v1 签名方案,现在改名为 RSASSA-PKCS1-v1_5 in PKCS1v2,实际上有four steps

#1 散列数据

#2 用 DER 编码的 DigestInfo ASN.1 结构中的哈希值和算法进行编码,这相当于为每个算法添加一个固定前缀

#3-5 预先填充 00 01 FF...(至少 8) 00 形式的填充

(8.2.1#2) 将结果视为数字 m,应用执行 m ^ d mod n 的 RSASP1(或为了验证 8.2.2#2 应用执行 s ^ e mod n 的 RSAVP1;这表示为在上述三个填充步骤之前但实际上也可以在之后)

向后密码操作仅执行或反转上述第三和第四步;您已经添加了第一步,但没有添加第二步,所以您的“解密”值实际上是一个 DigestInfo 结构,其中包含一些元数据、SHA1 算法的 OID,应该与数据对应的哈希值。

这种未能创建或删除 DigestInfo 结构的错误也是一个非常常见的错误和问题;在 https://crypto.stackexchange.com/questions/87006/why-is-data-signed-with-sha256-rsa-pkcs-and-digest-signed-with-rsa-pkcs-differen/#87022 查看我的列表。

但它不匹配。嵌入在恢复的 DigestInfo 中的哈希值与您对数据计算的哈希值不同(我也得到)。这强烈暗示你的数据和亚马逊签署的数据之间一些变化,但我不知道是什么;当然,您的数据从表面上看应该像 Alexa 请求。对不起:-)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-