C#AWS signatureV4对接Amazon接口

马上要放假了,需要抓紧时间测试对接一个三方接口,对方是使用Amazon服务的,国内不多见,能查的资(代)料(码),时间紧比较紧,也没有时间去啃Amazon的文档,主要我的英文水平也不行,于是粗略过了两遍文档后下载了Amazon的示例后填入AKSK进行测试,结果返回403 Forbidden,WTF,官网示例都不行?
然后找接口方要了一个示例,用JAVA写的很简单,直接调用了Amazon的sdk签名然后使用HttpClient进行请求,看起来很Easy啊,于是我也安装了AmazonSDK的nuget准备进行dotnet版本的开发。。。然后发现dotnet版本SDK是各种抽象类,而且方法名都差不多,跟Java版本的差很多,同名方法调用不了,在不啃文档花时间以我的能力直接用官方SDK还是太难了。之前不是下载了官方提供的示例吗,我这次把里面的签名算法拷贝了出来,自己调用自己写,仍旧报错。。。
没办法了,找到同事他用python写了一个,调用成功了,WTF?为啥啊
后来总结了一下,开始用的HttpClient,怕有什么问题,于是使用了RestSharp再次尝试,好了!!!
于是我又开始用了HttpClient,签名算法什么的一概不变,还是不行。。。
省略中间各种挣扎,最后使用抓包工具一点点比较两个请求,终于HttpClient成功了。
附上两版代码,另外还有一个坑,HttpClient在header中用Add添加Authorization会报错,需要用

 request.Headers.Authorization = new AuthenticationHeaderValue("AWS4-HMAC-SHA256", authorizationHeader);

RestSharp版本的

public class ApiClient
{private const string AccessKey = "AccessKey ";private const string SecretKey = "SecretKey ";private const string Region = "cn-northwest-1";private const string Service = "execute-api";private const string Url = "https://test.execute-api.cn-northwest-1.amazonaws.com.cn/api/test";private const string ApiKey = "ApiKey";public async Task CallApiAsync(){var client = new RestClient(Url);// 准备请求payload  var payload = new{key=value};var request = new RestRequest("", Method.Post);string jsonPayload = JsonSerializer.Serialize(payload);// 计算payload hash  string payloadHash;using (var sha256 = SHA256.Create()){var bytes = Encoding.UTF8.GetBytes(jsonPayload);var hash = sha256.ComputeHash(bytes);payloadHash = BitConverter.ToString(hash).Replace("-", "").ToLower();}// 获取当前UTC时间  var now = DateTime.UtcNow;var amzDate = now.ToString("yyyyMMddTHHmmssZ");// 设置请求头  request.AddStringBody(jsonPayload, DataFormat.Json);request.AddHeader("Content-Type", "application/json");request.AddHeader("Host", "test.execute-api.cn-northwest-1.amazonaws.com.cn");request.AddHeader("x-amz-content-sha256", payloadHash);request.AddHeader("x-amz-date", amzDate);  // 使用当前UTC时间  request.AddHeader("x-api-key", ApiKey);// 计算签名  var signer = new AWS4RequestSigner(AccessKey, SecretKey);await signer.Sign(request, Service, Region, payloadHash);try{var response = await client.ExecuteAsync(request);Console.WriteLine($"Status Code: {response.StatusCode}");Console.WriteLine($"Response: {response.Content}");}catch (Exception ex){Console.WriteLine($"Error: {ex.Message}");}}
}public class AWS4RequestSigner
{private readonly string _accessKey;private readonly string _secretKey;public AWS4RequestSigner(string accessKey, string secretKey){_accessKey = accessKey;_secretKey = secretKey;}public async Task Sign(RestRequest request, string service, string region, string payloadHash){var amzDate = request.Parameters.First(p => p.Name == "x-amz-date").Value.ToString();var dateStamp = amzDate.Substring(0, 8);// 准备签名所需的字符串  var credentialScope = $"{dateStamp}/{region}/{service}/aws4_request";// 计算签名  var stringToSign = CreateStringToSign(request, credentialScope, amzDate, payloadHash);var signingKey = GetSigningKey(dateStamp, region, service);var signature = CalculateSignature(signingKey, stringToSign);// 构造授权头  var authorizationHeader = $"AWS4-HMAC-SHA256 " +$"Credential={_accessKey}/{credentialScope}, " +$"SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-api-key, " +$"Signature={signature}";Console.WriteLine($"Authorization header: {authorizationHeader}");request.AddHeader("Authorization", authorizationHeader);}private string CreateStringToSign(RestRequest request, string credentialScope, string amzDate, string payloadHash){var canonicalRequest = CreateCanonicalRequest(request, payloadHash);return $"AWS4-HMAC-SHA256\n{amzDate}\n{credentialScope}\n" +CalculateHash(canonicalRequest);}private string CreateCanonicalRequest(RestRequest request, string payloadHash){var canonicalUrl = "/api/test";var canonicalQueryString = "";// 按照错误消息中的顺序构建规范头部  var contentType = request.Parameters.First(p => p.Name == "Content-Type").Value.ToString();var host = request.Parameters.First(p => p.Name == "Host").Value.ToString();var xAmzContentSha256 = request.Parameters.First(p => p.Name == "x-amz-content-sha256").Value.ToString();var xAmzDate = request.Parameters.First(p => p.Name == "x-amz-date").Value.ToString();var xApiKey = request.Parameters.First(p => p.Name == "x-api-key").Value.ToString();var canonicalHeaders =$"content-type:{contentType}\n" +$"host:{host}\n" +$"x-amz-content-sha256:{xAmzContentSha256}\n" +$"x-amz-date:{xAmzDate}\n" +$"x-api-key:{xApiKey}\n";var signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date;x-api-key";return $"POST\n{canonicalUrl}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}";}private byte[] GetSigningKey(string dateStamp, string region, string service){var kSecret = Encoding.UTF8.GetBytes($"AWS4{_secretKey}");var kDate = Sign(dateStamp, kSecret);var kRegion = Sign(region, kDate);var kService = Sign(service, kRegion);return Sign("aws4_request", kService);}private byte[] Sign(string data, byte[] key){using (var hmac = new HMACSHA256(key)){return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));}}private string CalculateSignature(byte[] signingKey, string stringToSign){using (var hmac = new HMACSHA256(signingKey)){var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));return BitConverter.ToString(signature).Replace("-", "").ToLower();}}private string CalculateHash(string text){using (var sha256 = SHA256.Create()){var bytes = Encoding.UTF8.GetBytes(text);var hash = sha256.ComputeHash(bytes);return BitConverter.ToString(hash).Replace("-", "").ToLower();}}
}

以下是HttpClient的

public class ApiClientHttpClient
{private const string AccessKey = "AccessKey";private const string SecretKey = "SecretKey";private const string Region = "cn-northwest-1";private const string Service = "execute-api";private const string Url = "https://test.execute-api.cn-northwest-1.amazonaws.com.cn/api/test";private const string ApiKey = "ApiKey";public async Task CallApiAsync(){using (var client = new HttpClient()){// 准备请求payload  var payload = new{Key=value};string jsonPayload = JsonSerializer.Serialize(payload);// 计算payload hash  string payloadHash = CalculateSha256(jsonPayload);// 获取当前UTC时间  var now = DateTime.UtcNow;var amzDate = now.ToString("yyyyMMddTHHmmssZ");var request = new HttpRequestMessage(HttpMethod.Post, Url){Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json") // 使用UTF8编码和"application/json"内容类型};// 移除默认添加的charset参数request.Content.Headers.ContentType.CharSet = null;//request.Headers.Accept.ParseAdd("application/json");//request.Headers.Accept.ParseAdd("text/json");//request.Headers.Accept.ParseAdd("text/x-json");//request.Headers.Accept.ParseAdd("text/javascript");//request.Headers.Accept.ParseAdd("application/xml");//request.Headers.Accept.ParseAdd("text/xml");//request.Headers.AcceptEncoding.ParseAdd("gzip");//request.Headers.AcceptEncoding.ParseAdd("deflate");//request.Headers.AcceptEncoding.ParseAdd("br");request.Headers.Host = "test.execute-api.cn-northwest-1.amazonaws.com.cn";request.Headers.Add("x-amz-content-sha256", payloadHash);request.Headers.Add("x-amz-date", amzDate);  // 使用当前UTC时间  request.Headers.Add("x-api-key", ApiKey);request.Headers.UserAgent.ParseAdd("test/1.0"); // 替换为你的应用名称和版本// 计算签名  var signer = new AWS4RequestSignerHttpClient(AccessKey, SecretKey);await signer.Sign(request, Service, Region, payloadHash, amzDate);try{var response = await client.SendAsync(request);Console.WriteLine($"Status Code: {response.StatusCode}");Console.WriteLine($"Response: {await response.Content.ReadAsStringAsync()}");}catch (Exception ex){Console.WriteLine($"Error: {ex.Message}");}}}private static string CalculateSha256(string input){using (SHA256 sha256 = SHA256.Create()){byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(input));return BitConverter.ToString(bytes).Replace("-", "").ToLower();}}
}public class AWS4RequestSignerHttpClient
{private readonly string _accessKey;private readonly string _secretKey;public AWS4RequestSignerHttpClient(string accessKey, string secretKey){_accessKey = accessKey;_secretKey = secretKey;}public async Task Sign(HttpRequestMessage request, string service, string region, string payloadHash, string amzDate){var dateStamp = amzDate.Substring(0, 8);var credentialScope = $"{dateStamp}/{region}/{service}/aws4_request";// 计算签名  var stringToSign = CreateStringToSign(request, credentialScope, amzDate, payloadHash);var signingKey = GetSigningKey(dateStamp, region, service);var signature = CalculateSignature(signingKey, stringToSign);// 构造授权头  var authorizationHeader = $"Credential={_accessKey}/{credentialScope}, " +$"SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-api-key, " +$"Signature={signature}";request.Headers.Authorization = new AuthenticationHeaderValue("AWS4-HMAC-SHA256", authorizationHeader);}private string CreateStringToSign(HttpRequestMessage request, string credentialScope, string amzDate, string payloadHash){var canonicalRequest = CreateCanonicalRequest(request, payloadHash);return $"AWS4-HMAC-SHA256\n{amzDate}\n{credentialScope}\n" + CalculateHash(canonicalRequest);}private string CreateCanonicalRequest(HttpRequestMessage request, string payloadHash){var canonicalUrl = "/api/test";var canonicalQueryString = "";// 按照错误消息中的顺序构建规范头部  var contentType = request.Content.Headers.ContentType?.ToString().Split(';').First();var host = request.Headers.Host;var xAmzContentSha256 = request.Headers.FirstOrDefault(h => h.Key == "x-amz-content-sha256").Value.First();var xAmzDate = request.Headers.FirstOrDefault(h => h.Key == "x-amz-date").Value.First();var xApiKey = request.Headers.FirstOrDefault(h => h.Key == "x-api-key").Value.First();var canonicalHeaders =$"content-type:{contentType}\n" +$"host:{host}\n" +$"x-amz-content-sha256:{xAmzContentSha256}\n" +$"x-amz-date:{xAmzDate}\n" +$"x-api-key:{xApiKey}\n";var signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date;x-api-key";return $"POST\n{canonicalUrl}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}";}private byte[] GetSigningKey(string dateStamp, string region, string service){var kSecret = Encoding.UTF8.GetBytes($"AWS4{_secretKey}");var kDate = Sign(dateStamp, kSecret);var kRegion = Sign(region, kDate);var kService = Sign(service, kRegion);return Sign("aws4_request", kService);}private byte[] Sign(string data, byte[] key){using (var hmac = new HMACSHA256(key)){return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));}}private string CalculateSignature(byte[] signingKey, string stringToSign){using (var hmac = new HMACSHA256(signingKey)){var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));return BitConverter.ToString(signature).Replace("-", "").ToLower();}}private string CalculateHash(string text){using (var sha256 = SHA256.Create()){var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(text));return BitConverter.ToString(bytes).Replace("-", "").ToLower();}}
}

主要的问题是Content-Type: application/json不要有charset=utf-8;第二个事需要一个User-Agent,其他的算法对了并且正确的添加到请求中就好了
另外,x-api-key只是我的接口要求字段

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/68341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

30289_SC65XX功能机MMI开发笔记(ums9117)

建立窗口步骤: 引入图片资源 放入图片 然后跑make pprj new job8 可能会有bug,宏定义 还会有开关灯报错,看命令行注释掉 接着把ture改成false 然后命令行new一遍,编译一遍没报错后 把编译器的win文件删掉, 再跑一遍虚拟机命令行…

“““【运用 R 语言里的“predict”函数针对 Cox 模型展开新数据的预测以及推理。】“““

主题与背景 本文主要介绍了如何在R语言中使用predict函数对已拟合的Cox比例风险模型进行新数据的预测和推理。Cox模型是一种常用的生存分析方法,用于评估多个因素对事件发生时间的影响。文章通过具体的代码示例展示了如何使用predict函数的不同参数来获取生存概率和…

Effective Objective-C 2.0 读书笔记—— objc_msgSend

Effective Objective-C 2.0 读书笔记—— objc_msgSend 文章目录 Effective Objective-C 2.0 读书笔记—— objc_msgSend引入——静态绑定和动态绑定OC之中动态绑定的实现方法签名方法列表 其他方法objc_msgSend_stretobjc_msgSend_fpretobjc_msgSendSuper 尾调用优化总结参考文…

验证二叉搜索树(力扣98)

根据二叉搜索树的特性,我们使用中序遍历,保证节点按从小到大的顺序遍历。既然要验证,就是看在中序遍历的条件下,各个节点的大小关系是否符合二叉搜索树的特性。双指针法和适合解决这个问题,一个指针指向当前节点&#…

【竞技宝】LPL:IG3-1击败RNG

北京时间1月26日,英雄联盟LPL2025正在如火如荼的进行之中,昨日共进行两场比赛。第二场比赛由RNG对阵IG。本场比赛,RNG在首局前期打出完美节奏后一直压制着IG拿下比赛,但此后的三局,IG发挥出自己擅长大乱斗的能力在团战…

web3py+flask+ganache的智能合约教育平台

最近在学习web3的接口文档,使用web3pyflaskganache写了一个简易的智能合约教育平台,语言用的是python,ganche直接使用的本地区块链网络,用web3py进行交互。 代码逻辑不难,可以私信或者到我的闲鱼号夏沫mds获取我的代码…

使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化

使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化 前言环境准备运行 Oracle Database 23ai Free 容器基本命令参数说明示例 注意事项高级配置参数说明 总结 前言 Oracle Database 23ai Free 是 Oracle 提供的免费版数据库,基于 Oracle …

JAVA(SpringBoot)集成Kafka实现消息发送和接收。

SpringBoot集成Kafka实现消息发送和接收。 一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者 君子之学贵一,一则明,明则有功。 一、Kafka 简介 Kafka 是由 Apache 软件基金会开发的一个开源流处理平台,最初由 Link…

Spring Boot 无缝集成SpringAI的函数调用模块

这是一个 完整的 Spring AI 函数调用实例&#xff0c;涵盖从函数定义、注册到实际调用的全流程&#xff0c;以「天气查询」功能为例&#xff0c;结合代码详细说明&#xff1a; 1. 环境准备 1.1 添加依赖 <!-- Spring AI OpenAI --> <dependency><groupId>o…

媒体新闻发稿要求有哪些?什么类型的稿件更好通过?

为了保证推送信息的内容质量&#xff0c;大型新闻媒体的审稿要求一向较为严格。尤其在商业推广的过程中&#xff0c;不少企业的宣传稿很难发布在这些大型新闻媒体平台上。 媒体新闻发稿要求有哪些&#xff1f;就让我们来了解下哪几类稿件更容易过审。 一、媒体新闻发稿要求有哪…

ui-automator定位官网文档下载及使用

一、ui-automator定位官网文档简介及下载 AndroidUiAutomator&#xff1a;移动端特有的定位方式&#xff0c;uiautomator是java实现的&#xff0c;定位类型必须写成java类型 官方地址&#xff1a;https://developer.android.com/training/testing/ui-automator.html#ui-autom…

ThreadLocal概述、解决SimpleDateFormat出现的异常、内存泄漏、弱引用、remove方法

①. ThreadLocal简介 ①. ThreadLocal是什么 ①. ThreadLocal本地线程变量,线程自带的变量副本(实现了每一个线程副本都有一个专属的本地变量,主要解决的就是让每一个线程绑定自己的值,自己用自己的,不跟别人争抢。通过使用get()和set()方法,获取默认值或将其值更改为当前线程…

总结8..

#include <stdio.h> // 定义结构体表示二叉树节点&#xff0c;包含左右子节点编号 struct node { int l; int r; } tree[100000]; // 全局变量记录二叉树最大深度&#xff0c;初始为0 int ans 0; // 深度优先搜索函数 // pos: 当前节点在数组中的位置&#xff0c…

科普篇 | “机架、塔式、刀片”三类服务器对比

一、引言 在互联网的世界里&#xff0c;服务器就像是默默运转的超级大脑&#xff0c;支撑着我们日常使用的各种网络服务。今天&#xff0c;咱们来聊聊服务器家族中的三位 “明星成员”&#xff1a;机架式服务器、塔式服务器和刀片式服务器。如果把互联网比作一座庞大的城市&…

动手学图神经网络(2):跆拳道俱乐部案例实战

动手学图神经网络(2):跆拳道俱乐部案例实战 在深度学习领域,图神经网络(GNNs)能将传统深度学习概念推广到不规则的图结构数据,使神经网络能够处理对象及其关系。将基于 PyTorch Geometric 库,一步步探索图神经网络的奥秘。 安装必要的包 首先, 安装所需的 Python 包…

【vue3组件】【大文件上传】【断点续传】支持文件分块上传,能够在上传过程中暂停、继续上传的组件

一、概述 本示例实现了一个基于 Vue3 和 TypeScript 的断点上传功能。该功能支持文件分块上传&#xff0c;能够在上传过程中暂停、继续上传&#xff0c;并且支持检测已经上传的分块&#xff0c;避免重复上传&#xff0c;提升上传效率。以下是关键的技术点与实现流程&#xff1…

OpenCV 版本不兼容导致的问题

问题和解决方案 今天运行如下代码&#xff0c;发生了意外的错误&#xff0c;代码如下&#xff0c;其中输入的 frame 来自于 OpenCV 开启数据流的读取 """ cap cv2.VideoCapture(RTSP_URL) print("链接视频流完成") while True:ret, frame cap.rea…

Day25-【13003】短文,什么是算法?如何衡量时间复杂度?什么是最优,平均时间复杂度?

文章目录 第二节概览什么是算法&#xff1f;算法的5个特性&#xff1f; 算法如何评估&#xff1f;时间指标如何衡量&#xff1f;算法的复杂度如何度量&#xff1f;算法开销上限和下限如何表示&#xff1f;什么是常数复杂度&#xff1f;线性操作&#xff1f;对数复杂度-线性对数…

python基础语法(3) -------- 学习笔记分享

目录: 1. 函数 1.1 语法格式 1.2 函数参数 1.3 函数返回值 1.4 变量的作用域 1.5 函数的执行过程 1.6 函数的链式调用 1.7 函数的嵌套调用 1.8 函数递归 1.9 参数默认值 1.10 函数的关键字传参 2. 列表和元组 2.1 列表和元组是啥 2.2 创建列表 2.3 访问下标 2.…

磐维数据库PanWeiDB2.0日常维护

磐维数据库简介 “中国移动磐维数据库”&#xff08;ChinaMobileDB&#xff09;&#xff0c;简称“磐维数据库”&#xff08;PanWeiDB&#xff09;。是中国移动信息技术中心首个基于中国本土开源数据库打造的面向ICT基础设施的自研数据库产品。 其产品内核能力基于华为 OpenG…