Добавление параметров запроса к неверному расчету подписи AWS4 - PullRequest
0 голосов
/ 01 апреля 2020

Я использую Orderhive API (https://orderhive.docs.apiary.io/#reference / product / product-catalog ), который использует аутентификацию подписи AWS4. Мне нужно получить все продукты с этой конечной точкой: https://api.orderhive.com/product/listing/flat?size=100&page=1, но всегда получаю ошибку 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.\n\nThe Canonical String for this request should have been\n'POST\n/product/listing/flat\npage=1&size=500

Ниже приведена текущая реализация

var endpoint = "https://api.orderhive.com/product/listing/flat?size=100&page=1";
var uri = new Uri(endpointUri);
var helpers = new HttpHelpers();
var result = helpers.InvokeHttpRequest(uri, "POST", PopulateHeader(endpoint, content, ""), content);

private Dictionary<string, string> PopulateHeader(string endpointUri, string content, string next_token)
{
    // precompute hash of the body content
    var contentHash = AWS4SignerBase.CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(content));
    var contentHashString = AWS4SignerBase.ToHexString(contentHash, true);

    var headers = new Dictionary<string, string>
        {
            {AWS4SignerBase.X_Amz_Content_SHA256, contentHashString},
            {"content-length", Encoding.UTF8.GetByteCount(content).ToString()},
            {"content-type", "application/json"},
            {"id_token", _credentails.id_token},
            {"X-Amz-Security-Token",  _credentails.session_token}
        };
    var uri = new Uri(endpointUri);
    var signer = new AWS4SignerForAuthorizationHeader
    {
        EndpointUri = uri,
        HttpMethod = "POST",
        Service = awsServiceName,
        Region = awsRegion
    };
    string queryString = new System.Uri(endpointUri).Query;
    var authorization = signer.ComputeSignature(headers,
                                                queryString,
                                                contentHashString,
                                                _credentails.access_key_id,
                                                _credentails.secret_key);

    // express authorization for this as a header
    headers.Add("Authorization", authorization);

    return headers;
}

public string ComputeSignature(IDictionary<string, string> headers,
                                       string queryParameters,
                                       string bodyHash,
                                       string awsAccessKey,
                                       string awsSecretKey)
{
    // first get the date and time for the subsequent request, and convert to ISO 8601 format
    // for use in signature generation
    var requestDateTime = DateTime.UtcNow;
    var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture);

    // update the headers with required 'x-amz-date' and 'host' values
    headers.Add(X_Amz_Date, dateTimeStamp);

    var hostHeader = EndpointUri.Host;
    if (!EndpointUri.IsDefaultPort)
        hostHeader += ":" + EndpointUri.Port;
    headers.Add("Host", hostHeader);

    // canonicalize the headers; we need the set of header names as well as the
    // names and values to go into the signature process
    var canonicalizedHeaderNames = CanonicalizeHeaderNames(headers);
    var canonicalizedHeaders = CanonicalizeHeaders(headers);

    // if any query string parameters have been supplied, canonicalize them
    // (note this sample assumes any required url encoding has been done already)
    var canonicalizedQueryParameters = string.Empty;
    if (!string.IsNullOrEmpty(queryParameters))
    {
        var paramDictionary = queryParameters.Split('&').Select(p => p.Split('='))
                                             .ToDictionary(nameval => nameval[0],
                                                           nameval => nameval.Length > 1
                                                                ? nameval[1] : "");

        var sb = new StringBuilder();
        var paramKeys = new List<string>(paramDictionary.Keys);
        paramKeys.Sort(StringComparer.Ordinal);
        foreach (var p in paramKeys)
        {
            if (sb.Length > 0)
                sb.Append("&");
            sb.AppendFormat("{0}={1}", p, paramDictionary[p]);
        }

        canonicalizedQueryParameters = sb.ToString();
    }

    // canonicalize the various components of the request
    var canonicalRequest = CanonicalizeRequest(EndpointUri,
                                               HttpMethod,
                                               canonicalizedQueryParameters,
                                               canonicalizedHeaderNames,
                                               canonicalizedHeaders,
                                               bodyHash);
    Console.WriteLine("\nCanonicalRequest:\n{0}", canonicalRequest);

    // generate a hash of the canonical request, to go into signature computation
    var canonicalRequestHashBytes
        = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest));

    // construct the string to be signed
    var stringToSign = new StringBuilder();

    var dateStamp = requestDateTime.ToString(DateStringFormat, CultureInfo.InvariantCulture);
    var scope = string.Format("{0}/{1}/{2}/{3}", 
                              dateStamp, 
                              Region, 
                              Service, 
                              TERMINATOR);

    stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope);
    stringToSign.Append(ToHexString(canonicalRequestHashBytes, true));

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

    // compute the signing key
    var kha = KeyedHashAlgorithm.Create(HMACSHA256);
    kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, Region, dateStamp, Service);

    // compute the AWS4 signature and return it
    var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString()));
    var signatureString = ToHexString(signature, true);
    Console.WriteLine("\nSignature:\n{0}", signatureString);

    var authString = new StringBuilder();
    authString.AppendFormat("{0}-{1} ", SCHEME, ALGORITHM);
    authString.AppendFormat("Credential={0}/{1}, ", awsAccessKey, scope);
    authString.AppendFormat("SignedHeaders={0}, ", canonicalizedHeaderNames);
    authString.AppendFormat("Signature={0}", signatureString);

    var authorization = authString.ToString();
    Console.WriteLine("\nAuthorization:\n{0}", authorization);

    return authorization;
}
...