我正在尝试调用 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 为同一请求(即用于计算且在计算之前被规范化的请求的一部分)计算的完全相同的哈希有效负载(对于带有正文的请求)散列有效载荷)。