C#中的AWS Signature v4密钥值存储KVS

时间:2019-05-08 10:05:08

标签: c# amazon-web-services amazon-s3 aws-sdk amazon-product-api

我正在尝试在C#中集成“ 签名带有签名版本4的AWS请求 ”。不幸的是,Amazon并未对C#的实现提供太多细节。我尝试了运气。但是它会抛出此错误消息

  

“远程服务器返回错误:(403)禁止。

注意:我已经在Postman上工作了。但是无法使其在C#中工作。 接收错误:消息=“远程服务器返回错误:(403)禁止。

我已经使用本文档对https://docs.amazonaws.cn/en_us/general/latest/gr/sigv4_signing.html进行了签名

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

        string authorization = Sign(hashedRequestPayload, "PUT", canonicalUri, canonicalQueriString);
        string requestDate = DateTime.ParseExact(DateTime.UtcNow.ToString(), "YYYYMMDD'T'HHMMSS'Z'", CultureInfo.InvariantCulture).ToString();

        WebRequest webRequest = WebRequest.Create(canonicalUri + canonicalQueriString);

        webRequest.Method = "PUT";
        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);
        using (WebResponse response = webRequest.GetResponse())
        {
            StreamReader responseReader = new StreamReader(response.GetResponseStream());
            var responseJson = responseReader.ReadToEnd();
        }

        return webRequest;
    }

    const string RegionName = "eu-west-1"; //This is the regionName
    const string ServiceName = "execute-api";
    const string Algorithm = "AWS4-HMAC-SHA256";
    const string ContentType = "application/json";
    const string Host = "<hostname>";
    const string SignedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date";

    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 = "";
        var secretKey = "";

        var dateStamp = currentDateTime.ToString("yyyyMMdd");
        //var requestDate = currentDateTime.ToString("YYYYMMDD'T'HHMMSS'Z");
        var requestDate = DateTime.ParseExact(currentDateTime.ToString(), "YYYYMMDD'T'HHMMSS'Z'", CultureInfo.InvariantCulture);
        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.ToString() }
    };

        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));
    }

1 个答案:

答案 0 :(得分:-1)

您愿意使用HttpClient而不是WebRequest吗?在那种情况下,我创建了一个名为AwsSignatureVersion4的NuGet程序包,该程序包已通过验证可与AWS API Gateway一起使用。