概述
AWS API Gateway的安全验证是保护API接口不被非法访问的重大机制。本文将详细介绍如何使用C#实现AWS API的签名验证过程(AWS Signature Version 4)。

核心组件
主要类结构
- ApiClient: 负责构造和发送HTTP请求
- AWS4RequestSigner: 处理AWS签名计算的核心类
关键参数
private const string AccessKey = "YOUR_ACCESS_KEY";
private const string SecretKey = "YOUR_SECRET_KEY";
private const string Region = "cn-northwest-1";
private const string Service = "execute-api";
签名验证流程
请求准备
构造请求payload
var payload = new
{
cnoc = "xxxxx",
cname = "青岛xxx有限公司",
bscope = "一般项目:工业自动控制系统装置销售...",
branch = "Sales Channels"
};
计算payload的SHA256哈希
string payloadHash;
using (var sha256 = SHA256.Create())
{
var bytes = Encoding.UTF8.GetBytes(jsonPayload);
var hash = sha256.ComputeHash(bytes);
payloadHash = BitConverter.ToString(hash).Replace("-", "").ToLower();
}
添加必要的请求头
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Host", "your-api-endpoint");
request.AddHeader("x-amz-content-sha256", payloadHash);
request.AddHeader("x-amz-date", amzDate);
request.AddHeader("x-api-key", ApiKey);
签名计算过程
创建规范请求(Canonical Request)
private string CreateCanonicalRequest(RestRequest request, string payloadHash)
{
// 按照指定格式组合HTTP方法、URI、查询字符串、请求头和payload hash
return $"POST
{canonicalUrl}
{canonicalQueryString}
{canonicalHeaders}
{signedHeaders}
{payloadHash}";
}
创建待签名字符串
private string CreateStringToSign(RestRequest request, string credentialScope, string amzDate, string payloadHash)
{
var canonicalRequest = CreateCanonicalRequest(request, payloadHash);
return $"AWS4-HMAC-SHA256
{amzDate}
{credentialScope}
" +
CalculateHash(canonicalRequest);
}
计算签名密钥
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 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();
}
}
认证头构造
最终的授权头格式如下:
var authorizationHeader = $"AWS4-HMAC-SHA256 " +
$"Credential={_accessKey}/{credentialScope}, " +
$"SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-api-key, " +
$"Signature={signature}";
完整代码
using System;
using System.Text;
using System.Text.Json;
using RestSharp;
using System.Security.Cryptography;
namespace AppAws
{
public class ApiClient
{
private const string AccessKey = "x";
private const string SecretKey = "xxx";
private const string Region = "cn-northwest-1";
private const string Service = "execute-api";
private const string Url = "https://xxx";
private const string ApiKey = "xxx";
public async Task CallApiAsync()
{
var client = new RestClient(Url);
// 准备请求payload
var payload = new
{
cnoc = "xx",
cname = "青岛xxx限公司",
bscope = "一般项目:xxx",
branch = "Sales Channels"
};
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", "ug6otu1t66.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}");
// 输出当前使用的时间戳,用于调试
Console.WriteLine($"Used timestamp: {amzDate}");
}
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}";
request.AddHeader("Authorization", authorizationHeader);
}
private string CreateStringToSign(RestRequest request, string credentialScope, string amzDate, string payloadHash)
{
var canonicalRequest = CreateCanonicalRequest(request, payloadHash);
return $"AWS4-HMAC-SHA256
{amzDate}
{credentialScope}
" +
CalculateHash(canonicalRequest);
}
private string CreateCanonicalRequest(RestRequest request, string payloadHash)
{
var canonicalUrl = "/api/company-role-pred2";
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}
" +
$"host:{host}
" +
$"x-amz-content-sha256:{xAmzContentSha256}
" +
$"x-amz-date:{xAmzDate}
" +
$"x-api-key:{xApiKey}
";
var signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date;x-api-key";
return $"POST
{canonicalUrl}
{canonicalQueryString}
{canonicalHeaders}
{signedHeaders}
{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();
}
}
}
}
最佳实践和注意事项
时间同步
- 确保服务器时间准确,与AWS服务器时间偏差不超过15分钟
- 使用UTC时间进行签名计算
安全思考
- 妥善保管AccessKey和SecretKey
- 提议使用环境变量或配置文件存储敏感信息
- 定期轮换密钥
错误处理
- 实现完整的错误处理机制
- 记录详细的日志信息便于调试
性能优化
- 可以缓存签名密钥(signing key)
- 注意请求头的大小写敏感性
常见问题排查
签名不匹配
- 检查请求头的排序是否正确
- 确认换行符使用
而不是 - 验证时间戳格式
请求被拒绝
- 检查API密钥是否正确
- 确认账号权限配置
- 验证区域设置是否正确
小结
AWS API的签名验证虽然流程较为复杂,但通过正的确 现这些步骤,可以确保API调用的安全性。本文提供的代码实现了完整的签名验证流程,可以作为实际项目开发的参考。提议在使用时根据具体需求进行适当调整和优化。
特别提醒:示例代码中的密钥和API端点已经过脱敏处理,实际使用时请替换为自己的配置信息。同时,提议遵循AWS的安全最佳实践,合理保护密钥信息。
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END














暂无评论内容