当前位置: 首页 > news >正文

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.lryc.cn/news/527849.html

相关文章:

  • C语言操作符(下)
  • 学习资料收藏 游戏开发
  • 我的2024年总结
  • freeswitch在centos上编译过程
  • docker如何查看容器启动命令(已运行的容器)
  • 正则表达式以及Qt中的使用
  • 当高兴、尊重和优雅三位一体是什么情况吗?
  • Vue 3 中的 TypeScript:接口、自定义类型与泛型
  • 【Super Tilemap Editor使用详解】(十六):高级主题:深入理解 Super Tilemap Editor
  • 如何运用python爬虫爬取知网相关内容信息?
  • 2025年数学建模美赛 A题分析(2)楼梯使用频率数学模型
  • 云原生:构建现代化应用的基石
  • 18.Word:数据库培训课程❗【34】
  • 批量创建ES索引
  • RoboVLM——通用机器人策略的VLA设计哲学:如何选择骨干网络、如何构建VLA架构、何时添加跨本体数据
  • 25美赛ABCDEF题详细建模过程+可视化图表+参考论文+写作模版+数据预处理
  • 基于RIP的MGRE VPN综合实验
  • 如何获取小程序的code在uniapp开发中
  • 【Linux】 冯诺依曼体系与计算机系统架构全解
  • RDMA 工作原理 | 支持 RDMA 的网络协议
  • Autosar-Os是怎么运行的?(多核系统运行)
  • golang命令大全4--测试与调试
  • 第27篇 基于ARM A9处理器用C语言实现中断<三>
  • linux下使用脚本实现对进程的内存占用自动化监测
  • 安宝特方案 | 智能培训:安宝特AR如何提升企业技能培训的效率与互动性
  • golang通过AutoMigrate方法自动创建table详解
  • 【信息系统项目管理师-选择真题】2013上半年综合知识答案和详解
  • 智能调度体系与自动驾驶技术优化运输配送效率的研究——兼论开源AI智能名片2+1链动模式S2B2C商城小程序的应用潜力
  • 【软件测试项目实战 】淘宝网:商品购买功能测试
  • [牛客]公交线路(dijkstra+链式前向星)