3

I am generating a presigned URL server-side to allow my client application to upload a file directly to the S3 bucket. Everything works fine unless the client application is running on a computer in a timezone that is technically a day ahead of my server clock.

I can recreate the issue locally by setting my system clock ahead to a timezone on the next day.

Here is how I am generating the presigned URL using the .NET SDK (I originally had DateTime.Now instead of UTCNow):

var request = new GetPreSignedUrlRequest
{
 BucketName = bucketName,
 Key = objectName,
 Verb = HttpVerb.PUT,
 Expires = DateTime.UtcNow.AddDays(5),
 ContentType = "application/octet-stream"
};

request.Headers["x-amz-acl"] = "bucket-owner-full-control";
request.Metadata.Add("call", JsonConvert.SerializeObject(call).ToString());

return client.GetPreSignedURL(request);

and then I am using that presigned URL in the client application like this:

using (var fileStream = new FileStream(recordingPath, FileMode.Open))
using (var client = new WebClient())
{
    HttpContent fileStreamContent = new StreamContent(fileStream);
    var bytes = await fileStreamContent.ReadAsByteArrayAsync();
    client.Headers.Add("Content-Type", "application/octet-stream");
    //include metadata in PUT request
    client.Headers.Add("x-amz-meta-call", JsonConvert.SerializeObject(Call));

    await client.UploadDataTaskAsync(new Uri(presignedUrl), "PUT", bytes);
}

Here is the error I am receiving from AWS:

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>{access}</AWSAccessKeyId><StringToSign>....


The requests appear mostly identical to me in Fiddler.

Works:

PUT https://{bucketname}.s3.amazonaws.com/1c849c76-dd2a-4ff7-aad7-23ec7e9ddd45_encoded.opus?X-Amz-Expires=18000&x-amz-security-token={security_token}&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential={cred}&X-Amz-Date=20190312T021419Z&X-Amz-SignedHeaders=content-type;host;x-amz-acl;x-amz-meta-call;x-amz-security-token&X-Amz-Signature={sig} HTTP/1.1
x-amz-meta-call: {json_string}
x-amz-acl: bucket-owner-full-control
Content-Type: application/octet-stream
Host: {bucketname}.s3.amazonaws.com
Content-Length: 28289
Expect: 100-continue

{file}

Does not work:

PUT https://{bucketname}.s3.amazonaws.com/4cca3ec3-9f3f-4ba4-9d81-6336090610c0_encoded.opus?X-Amz-Expires=18000&x-amz-security-token={security_token}&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential={credentials}&X-Amz-Date=20190312T021541Z&X-Amz-SignedHeaders=content-type;host;x-amz-acl;x-amz-meta-call;x-amz-security-token&X-Amz-Signature={sig} HTTP/1.1
x-amz-meta-call: {json_string}
x-amz-acl: bucket-owner-full-control
Content-Type: application/octet-stream
Host: {bucketname}.s3.amazonaws.com
Content-Length: 18714
Expect: 100-continue


{file}

In both scenarios, the presigned URL has the same x-amz-date parameter generated. I have even tried parsing out the x-amz-date parameter from the URL and explicitly setting it as a header in my PUT but that did not work either.

What am I missing?

4

2 回答 2

10

It turned out to me that I was using a different version of the signature. v4 worked perfectly for me.

In JS, require S3 as

const s3 = new AWS.S3({
  signatureVersion: 'v4'
});
于 2019-12-05T08:42:27.087 回答
0

所以问题最终出现在元数据中。在我们的设置中,我们让客户端应用程序将 JSON 字符串连同文件一起发布到我们的 API,以生成预签名 URL。我们使用 Json.net 反序列化为 C# 类:

var call = JsonConvert.DeserializeObject<Call>(request.Params["metadata"]);

显然,此调用将 Json 中的任何时间戳转换为本地时间。这意味着我们将使用 API 服务器本地的元数据时间戳对 URL 进行签名,但实际上将带有本地元数据时间戳的文件上传到客户端。这种差异就是计算的签名不同的原因。

于 2019-03-12T15:12:45.183 回答