0

我正在尝试调用 Amazon Selling Partner API Token API 来获取 RDT 令牌。我有一个正在向 Orders API 发出成功 GET 请求的工作程序,但对 Orders API 的 REST 请求仅包含标头和 URL 参数。令牌 API 请求还包含(另外)HTTP 正文,其中保存了受限资源列表。

亚马逊回复

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 Canonical String for this request should have been
'GET
 /tokens/2021-03-01/restrictedDataToken
 ...
 host;x-amz-access-token;x-amz-date\n
 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
 ...
 The String-to-Sign should have been
 ...
 ...

在我的令牌 API 程序中。e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855是我问题的关键。这是哈希paylod(见下一个链接中的图片),它是来自初始标题集、URL 参数和正文内容的计算机。我的程序计算的哈希有效负载与亚马逊返回并接受没有正文的请求相同。但是亚马逊和我的程序的哈希有效负载对于带有正文的请求是不同的。因此,我得出结论,我的程序添加和散列正文与亚马逊不同,这就是亚马逊不接受我的令牌 API 请求的原因。

AWS - 真正的规范请求是什么?有很好的图片,如何计算对亚马逊服务的完整请求。

我正在计算并使用 C# 代码添加 HTTP 正文:

        Model.RestrictedResource restrictedResource = new Model.RestrictedResource(
            Model.RestrictedResource.MethodEnum.GET,
            "/orders/v0/orders",
            new List<String> {"buyerInfo", "shippingAddress"});
        List<Model.RestrictedResource> restrictedResources = new List<Model.RestrictedResource>();
            restrictedResources.Add(restrictedResource);
            Model.CreateRestrictedDataTokenRequest request = new Model.CreateRestrictedDataTokenRequest("", restrictedResources);

            IRestRequest restRequest = new RestRequest(rdt_resource, Method.GET);
            restRequest.AddParameter("MarketplaceIds", marketplace_id, ParameterType.QueryString);
            restRequest.AddJsonBody(Serialize(request));
            //restRequest.AddBody(Serialize(request)); //Alternative solution, the same Amazon error about differing hashed payload values

这是计算散列的 CanonicalRequest 字符串的代码:

   public IRestRequest Sign(IRestRequest request, string host)
    {
        DateTime signingDate = AwsSignerHelper.InitializeHeaders(request, host);
        string signedHeaders = AwsSignerHelper.ExtractSignedHeaders(request);

        string hashedCanonicalRequest = CreateCanonicalRequest(request, signedHeaders);

        string stringToSign = AwsSignerHelper.BuildStringToSign(signingDate,
                                                                hashedCanonicalRequest,
                                                                awsCredentials.Region);

        string signature = AwsSignerHelper.CalculateSignature(stringToSign,
                                                              signingDate,
                                                              awsCredentials.SecretKey,
                                                              awsCredentials.Region);

        AwsSignerHelper.AddSignature(request,
                                     awsCredentials.AccessKeyId,
                                     signedHeaders,
                                     signature,
                                     awsCredentials.Region,
                                     signingDate);

        return request;
    }

    private string CreateCanonicalRequest(IRestRequest restRequest, string signedHeaders)
    {
        var canonicalizedRequest = new StringBuilder();
        //Request Method
        canonicalizedRequest.AppendFormat("{0}\n", restRequest.Method);

        //CanonicalURI
        canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalURIParameters(restRequest.Resource));

        //CanonicalQueryString
        canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalQueryString(restRequest));

        //CanonicalHeaders
        canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalHeaders(restRequest));

        //SignedHeaders
        canonicalizedRequest.AppendFormat("{0}\n", signedHeaders);

        // Hash(digest) the payload in the body
        canonicalizedRequest.AppendFormat(AwsSignerHelper.HashRequestBody(restRequest));

        string canonicalRequest = canonicalizedRequest.ToString();

        //Create a digest(hash) of the canonical request
        return Utils.ToHex(Utils.Hash(canonicalRequest));
    }

    //This seems to be the key code - how body is extracted and hashed
    public virtual string HashRequestBody(IRestRequest request)
    {
        Parameter body = request.Parameters.FirstOrDefault(parameter => ParameterType.RequestBody.Equals(parameter.Type));
        string value = body != null ? body.Value.ToString() : string.Empty;
        return Utils.ToHex(Utils.Hash(value));
    }

所以,我失踪了,我不是在某个地方遵循亚马逊惯例,而是在哪里。有很多事情可能会变坏:1)也许restRequest.AddJsonBody(Serialize(request));应该以不同的方式做;2)也许HashRequestBody应该以不同的方式实现(我在互联网的某个地方找到了代码,或者是由 Swagger 从销售伙伴 API 规范生成的。

如何调整代码以生成与 Amazon 为同一请求(即用于计算且在计算之前被规范化的请求的一部分)计算的完全相同的哈希有效负载(对于带有正文的请求)散列有效载荷)。

4

1 回答 1

0

我设法解决了。应该将问题中的代码与这些修改一起使用:

设置 TargetApplication 可能很重要:

 Model.CreateRestrictedDataTokenRequest request = new Model.CreateRestrictedDataTokenRequest("", restrictedResources);
 request.TargetApplication = "MyApplication";

并且应该使用不同的类型代码进行序列化和添加主体。最终重要的是使用 POST 请求,而不是 GET 请求:

IRestRequest restRequest = new RestRequest(rdt_resource, Method.POST);
            restRequest.AddParameter("MarketplaceIds", marketplace_id, ParameterType.QueryString);
            String postBody = SerializeBody(request);
            restRequest.AddParameter("application/json", postBody, ParameterType.RequestBody);
...
...
    public String SerializeBody(object obj)
    {
        try
        {
            return obj != null ? JsonConvert.SerializeObject(obj) : null;
        }
        catch (Exception e)
        {
            throw new Exception(e.Message);
        }
    }

不,我收到错误消息:

Application does not have access to one or more requested data elements: [buyerInfo, shippingAddress]

但这很好,因为从技术上讲,注册用户和应用程序已被识别,并且请求及其签名格式正确。

于 2021-12-03T11:33:16.943 回答