【问题标题】:AWS API Gateway SignatureAWS API 网关签名
【发布时间】:2022-03-25 04:39:25
【问题描述】:

我正在尝试签署我对亚马逊网关的请求。但是每次我尝试发送 POST 请求时,它都会告诉我我的签名已过期。任何想法将不胜感激。

【问题讨论】:

  • 发布您的签名请求示例,其中包含 aws 访问密钥 ID 的一部分以及您认为敏感的任何其他内容被遮盖。 (签名本身不包含任何“可逆”。)

标签: c# .net amazon-web-services httpwebrequest aws-api-gateway


【解决方案1】:

你在获得时间或类似的事情上遇到了一些问题。我遇到了有效载荷的问题。因此,如果您发出 GET 请求,您的有效负载是 EMPTY STRING。否则它应该是散列的 Json 对象。这是我如何在我的应用程序中执行此操作的示例。代码可以是原始的,但我 100000% 可以工作,因为我每天都在使用它。

const string RegionName = "eu-west-1"; //This is the regionName
const string ServiceName = "apigateway";
const string Algorithm = "AWS4-HMAC-SHA256";
const string ContentType = "application/json";
const string Host = "apigateway.eu-west-1.amazonaws.com";
const string SignedHeaders = "content-type;host;x-amz-date";

public static WebRequest RequestGet(string canonicalUri, string canonicalQueriString, string jsonString) {
    string hashedRequestPayload = CreateRequestPayload("");

    string authorization = Sign(hashedRequestPayload, "GET", canonicalUri, canonicalQueriString);
    string requestDate = DateTime.UtcNow.ToString("yyyyMMddTHHmmss") + "Z";

    WebRequest webRequest = WebRequest.Create("https://" + Host + canonicalUri);

    webRequest.Method = "GET";
    webRequest.ContentType = ContentType;
    webRequest.Headers.Add("X-Amz-date", requestDate);
    webRequest.Headers.Add("Authorization", authorization);
    webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload);


    return webRequest;
}

public static WebRequest RequestPost(string canonicalUri, string canonicalQueriString, string jsonString)
{
    string hashedRequestPayload = CreateRequestPayload(jsonString);

    string authorization = Sign(hashedRequestPayload, "POST", canonicalUri, canonicalQueriString);
    string requestDate = DateTime.UtcNow.ToString("yyyyMMddTHHmmss") + "Z";

    WebRequest webRequest = WebRequest.Create("https://" + Host + canonicalUri);

    webRequest.Timeout = 20000;
    webRequest.Method = "POST";
    webRequest.ContentType = ContentType;
    webRequest.Headers.Add("X-Amz-date", requestDate);
    webRequest.Headers.Add("Authorization", authorization);
    webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload);
    webRequest.ContentLength = jsonString.Length;

    ASCIIEncoding encoding = new ASCIIEncoding();
    byte[] data = encoding.GetBytes(jsonString);

    Stream newStream = webRequest.GetRequestStream();
    newStream.Write(data, 0, data.Length);


    return webRequest;
}

private static string CreateRequestPayload(string jsonString) {
    //Here should be JSON object of the model we are sending with POST request
    //var jsonToSerialize = new { Data = String.Empty };

    //We parse empty string to the serializer if we are makeing GET request
    //string requestPayload = new JavaScriptSerializer().Serialize(jsonToSerialize);
    string hashedRequestPayload = HexEncode(Hash(ToBytes(jsonString)));

    return hashedRequestPayload;
}

private static string Sign(string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString) {
    var currentDateTime = DateTime.UtcNow;
    var accessKey = //Here place your app ACCESS_KEY
    var secretKey = //Here is a place for you app SECRET_KEY

    var dateStamp = currentDateTime.ToString("yyyyMMdd");
    var requestDate = currentDateTime.ToString("yyyyMMddTHHmmss") + "Z";
    var credentialScope = string.Format("{0}/{1}/{2}/aws4_request", dateStamp, RegionName, ServiceName);

    var headers = new SortedDictionary < string, string > {
            { "content-type", ContentType },
            { "host", Host  }, 
            { "x-amz-date", requestDate }
        };

    string canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";

    // Task 1: Create a Canonical Request For Signature Version 4
    string canonicalRequest = requestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + SignedHeaders + "\n" + hashedRequestPayload;
    string hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));

    // Task 2: Create a String to Sign for Signature Version 4
    string stringToSign = Algorithm + "\n" + requestDate + "\n" + credentialScope + "\n" + hashedCanonicalRequest;

    // Task 3: Calculate the AWS Signature Version 4
    byte[] signingKey = GetSignatureKey(secretKey, dateStamp, RegionName, ServiceName);
    string signature = HexEncode(HmacSha256(stringToSign, signingKey));

    // Task 4: Prepare a signed request
    // Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature

    string authorization = string.Format("{0} Credential={1}/{2}/{3}/{4}/aws4_request, SignedHeaders={5}, Signature={6}",
    Algorithm, accessKey, dateStamp, RegionName, ServiceName, SignedHeaders, signature);

    return authorization;
}

private static byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName) {
    byte[] kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
    byte[] kRegion = HmacSha256(regionName, kDate);
    byte[] kService = HmacSha256(serviceName, kRegion);
    return HmacSha256("aws4_request", kService);
}

private static byte[] ToBytes(string str) {
    return Encoding.UTF8.GetBytes(str.ToCharArray());
}

private static string HexEncode(byte[] bytes) {
    return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
}

private static byte[] Hash(byte[] bytes) {
    return SHA256.Create().ComputeHash(bytes);
}

private static byte[] HmacSha256(string data, byte[] key) {
    return new HMACSHA256(key).ComputeHash(ToBytes(data));
}

例如,如果我想获取部署在网关中的所有 API,我会这样做:

using(WebResponse response = webRequest.GetResponse()) {
    StreamReader responseReader = new   StreamReader(response.GetResponseStream());
    string responseJson = responseReader.ReadToEnd();
} catch (WebException) {
    //Doing something when exception has been thrown
}

这是创建 API 密钥的有趣部分。首先,您需要制作原始有效负载,然后将其传递给我上面给您的方法:

string payload = "{ \"name\" : \"" + name + "\", \"description\" : \"" + description.Trim() + "\", \"enabled\" : \"True\", \"stageKeys\" : [ ] }";

WebRequest webRequest = RequestSignerAWS.RequestPost("/apikeys", "", payload);

并确保您获得了创建请求的时间,因为这会导致您遇到问题。

【讨论】:

  • 谢谢。我会检查你的代码,但很可能唱歌已经过期了,因为我没有在邮递员的标题中设置时间。
  • 如何发布到 API Gateway 端点以创建资源。我的 api 由 AWS lambda 支持,我使用 cognito 登录 API。我使用 API Gateway SDK 在 Javascript 中使用它,如何使用 c# 调用端点?
  • 感谢分享代码!对于我的情况,我必须将 ServiceName 更改为“execute-api”,并且我的主机字符串有很大不同(这是您要访问的 API 的已部署阶段上的调用 URL)。另一件重要的事情是添加好的代码以在返回错误时读取响应 - 亚马逊发回非常有用的消息,以指出您的签名有什么问题
  • 嗨,我使用相同的代码,我在 AWS API 网关方法中添加了映射模板,但是在 lambda 函数中我没有获取参数?
  • 嗨,我正在使用相同的代码调用 aws api,但我收到 403:forbidden 异常。但是当我通过邮递员访问该 api 时,它已成功执行
【解决方案2】:

您可以查看或使用此项目中的代码来向 API 网关发送请求: https://github.com/ronenfe/Addons.AwsSdk 它使用此代码进行签名: https://github.com/tsibelman/aws-signer-v4-dot-net.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-31
    • 1970-01-01
    • 2016-03-31
    • 2018-09-05
    • 1970-01-01
    • 2023-03-25
    • 2018-07-24
    • 2016-12-13
    相关资源
    最近更新 更多