c# - AWS API 网关签名

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

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

最佳答案

您在获得时间或类似问题时遇到了一些问题。我遇到了有效载荷的问题。因此,如果您发出 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 key 的有趣部分。首先,您需要制作原始负载,然后将其传递给我在上面给您的方法:

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

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

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

关于c# - AWS API 网关签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32906755/

相关文章:

c# - 什么时候应该使用 `out` 参数而不是返回复杂类型?

c# - 防止使用先前输入的值自动填充文本框

c# - 如何在选择游戏对象时将 Material 分配给游戏对象,并在使用 C# 取消选择游戏对象时将其更改回原始 Material ?

c# - 如何全局声明样式并在 uwp 中的应用程序中使用该样式

javascript - 是否可以将动态变量传递给 process.env 来获取 env 变量?例如 process.env ['variable name' ]

node.js - Lambda Axios 发布到第二个 API 奇怪的异步行为

C# - 如果列表包含列表

c# - C++ CLI 中的 C# 动态关键字等效项是什么?

c# - 如何在母版页内的用户控件中获取内容页名称?

node.js - Aws Lambda 响应错误