AWS API网关签名

时间:2015-10-02 12:01:41

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

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

1 个答案:

答案 0 :(得分:9)

你有时间或类似的问题。我遇到了有效载荷的问题。因此,如果您要进行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));
}

因此,例如,如果我想获得部署在Gateway中的所有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);

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