Orderhive AWS4 签名备注匹配

Orderhive AWS4 Signature not match

我正在尝试连接到 AWS4 签名方法进行身份验证。 (https://orderhive.docs.apiary.io/#introduction/api-requirements/end-point)

我的 id_token 和 refresh_token 检索 access_key_id、secret_key 和 session_token。 但是当我尝试检索仓库等信息时,每次都会收到:

"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

The String-to-Sign should have been
'AWS4-HMAC-SHA256 20211217T160055Z 20211217/us-east-1/execute-api/aws4_request 8e3dbc663f97508406c4825b74a647765022ae021fa224754701722b7bcf2288'

我正在使用这段代码,就像其他人在我之前在某些示例中所做的那样。

 public const string SCHEME = "AWS4";
 public const string ALGORITHM = "HMAC-SHA256";
 public const string TERMINATOR = "aws4_request";
 public const string ISO8601BasicFormat = "yyyyMMddTHHmmssZ";
 public const string DateStringFormat = "yyyyMMdd";
 public const string X_Amz_Date = "X-Amz-Date";
 public const string RegionName = "us-east-1"; 
 public const string ServiceName = "execute-api";
 public const string ContentType = "application/json";
 public const string SignedHeaders = "content-type;host;id_token;x-amz-date;x-amz-security-token";
 public const string X_Amz_Content_SHA256 = "X-Amz-Content-SHA256";
 private Account account;

 public void GetWarehouse()
    {
        try
        {
            WebRequest webRequest = RequestGet("/setup/warehouse", "", "");
            using (WebResponse response = webRequest.GetResponse())
            {
                StreamReader responseReader = new StreamReader(response.GetResponseStream());
                string jsonResponse = responseReader.ReadToEnd();
            }
        }
        catch (Exception ex)
        {}
    }
 public WebRequest RequestGet(string canonicalUri, string canonicalQueriString, string jsonString)
    {
        string hashedRequestPayload = CreateRequestPayload("");

        string authorization = Sign(hashedRequestPayload, "GET", canonicalUri, canonicalQueriString);
        string requestDate = DateTime.UtcNow.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture);

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

        webRequest.Method = "GET";
        webRequest.ContentType = ContentType;
        webRequest.Headers.Add("id_token", account.id_token);
        webRequest.Headers.Add("X-Amz-Security-Token", account.session_token);
        webRequest.Headers.Add(X_Amz_Date, requestDate);
        webRequest.Headers.Add("Authorization", authorization);
        webRequest.Headers.Add(X_Amz_Content_SHA256, hashedRequestPayload);

        return webRequest;
    }
 private string Sign(string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString)
    {
        var currentDateTime = DateTime.UtcNow;
        var accessKey = account.access_key_id;
        var secretKey = account.secret_key;

        var dateStamp = currentDateTime.ToString(DateStringFormat);
        var requestDate = currentDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture);            
        var credentialScope = string.Format("{0}/{1}/{2}/{3}", dateStamp, RegionName, ServiceName, TERMINATOR);

        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;
        Console.WriteLine("\nCanonicalRequest:\n{0}", canonicalRequest);
        string hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));

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

        Console.WriteLine("\nStringToSign:\n{0}", stringToSign);

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

        string signature = HexEncode(HmacSHA256(stringToSign, signingKey));
        Console.WriteLine("\nSignature:\n{0}", signature);
        // 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}/{5}, SignedHeaders={6}, Signature={7}", SCHEME + "-" + ALGORITHM, accessKey, dateStamp, RegionName, ServiceName, TERMINATOR, SignedHeaders, signature);

        Console.WriteLine("\nAuthorization:\n{0}", authorization);
        return authorization;
    }

    private byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName)
    {
        byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
        byte[] kDate = HmacSHA256(dateStamp, kSecret);
        byte[] kRegion = HmacSHA256(regionName, kDate);
        byte[] kService = HmacSHA256(serviceName, kRegion);
        byte[] kSigning = HmacSHA256(TERMINATOR, kService);

        return kSigning;
    }
    static byte[] HmacSHA256(String data, byte[] key)
    {
        string algorithm = "HmacSHA256";
        KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
        kha.Key = key;

        return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
    }

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

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

有人知道我做错了什么吗? 谢谢!

最后,我改变了我的 Sign 方法:

public async Task<HttpRequestMessage> Sign(HttpRequestMessage request, string service, string region, TimeSpan? timeOffset = null)
    {
        if (!IsValidRequest(request, service, region))
            throw new Exception("Invalid request!");

        if (request.Headers.Host == null)
        {
            request.Headers.Host = request.RequestUri.Host;
        }

        var payloadHash = await GetPayloadHash(request.Content);
        if (request.Headers.Contains("x-amz-content-sha256") == false)
            request.Headers.Add("x-amz-content-sha256", payloadHash);
        
        if (request.Headers.Contains("x-amz-security-token") == false)
            request.Headers.Add("x-amz-security-token", Account.session_token);

        var t = DateTimeOffset.UtcNow;
        if (timeOffset.HasValue)
            t = t.Add(timeOffset.Value);

        var amzDate = t.ToString(ISO8601BasicFormat);
        var dateStamp = t.ToString(DateStringFormat);
        request.Headers.Add("x-amz-date", amzDate);

        var canonicalUri = string.Join("/", request.RequestUri.AbsolutePath.Split('/').Select(Uri.EscapeDataString));

        var canonicalQueryParams = GetCanonicalQueryParams(request);

        var signedHeadersList = new List<string>();
        var canonicalHeaders = new StringBuilder();
        foreach (var header in request.Headers.OrderBy(a => a.Key.ToLowerInvariant(), StringComparer.OrdinalIgnoreCase))
        {                
            var headerValue = (header.Key == "Host") ? EndPoint : string.Join(",", header.Value.Select(s => s.Trim()));
            canonicalHeaders.Append($"{header.Key.ToLowerInvariant()}:{headerValue}\n");
            signedHeadersList.Add(header.Key.ToLowerInvariant());
        }
        var signedHeaders = string.Join(";", signedHeadersList);

        string canonicalRequest = $"{request.Method}\n{canonicalUri}\n{canonicalQueryParams}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}";
        string hashedCanonicalRequest = Hash(Encoding.UTF8.GetBytes(canonicalRequest.ToString()));

        var credentialScope = $"{dateStamp }/{region}/{service}/{Terminator}";
        var signingKey = GetSignatureKey(Account.secret_key, dateStamp, region, service);

        var stringToSign = $"{AWS4AlgorithmTag}\n{amzDate}\n{credentialScope}\n{hashedCanonicalRequest}";
        var signature = ToHexString(HmacSha256(signingKey, stringToSign));

        string authorization = $"{AWS4AlgorithmTag} Credential={Account.access_key_id}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}";
        request.Headers.TryAddWithoutValidation("Authorization", authorization);

        return request;
    }

现在一切正常。非常重要的一点是:在测试中,Host 需要是好的,https://api.orderhive.com/。我上一个问题的问题是,当我构建 header 时,不知为何,他在 AWS 计算签名时出错了。希望这对遇到此问题的其他人有所帮助。