Search code examples
.netamazon-s3aws-sdkamazon-kms

Generating a pre-signed URL for S3 object with KMS encryption - what am I doing wrong?


I'm using the AWS SDK v2.3.44.0 for .NET 4.0 and am trying to generate a pre-signed URL for clients to download objects. All of the objects are stored in an S3 bucket and encrypted using the aws:kms server side encryption method.

I'm using the AmazonS3Client's GetPreSignedURL method to generate the URL.

My AmazonS3Client object is created like so:

var client = new AmazonS3Client(new AmazonS3Config
{
    RegionEndpoint = RegionEndpoint.USEast1,
    SignatureVersion = "4"
});

My GetPresignedUrlRequest object is created like so:

var request = new GetPreSignedUrlRequest
{
    BucketName = bucket,
    Key = key,
    Expires = expires,
    ServerSideEncryptionMethod = ServerSideEncryptionMethod.AWSKMS
 };

Now when I run client.GetPreSignedURL(request) it generates a URL like so (formatted for readability and sanitized):

https://{bucket}.s3.amazonaws.com/{key}?AWSAccessKeyId={access key}
    &Expires={timestamp}
    &Signature=AWS4-HMAC-SHA256%20Credential%3D{access key}%2F20150626%2Fus-east-1%2Fs3%2Faws4_request%2C%20SignedHeaders%3Dhost%3Bx-amz-content-sha256%3Bx-amz-date%3Bx-amz-server-side-encryption%2C%20Signature%3D{signature}

When navigating to this URL, I get an XML breakdown of an error: SignatureDoesNotMatch.

This URL looks different from the one Amazon describes in their docs. Also, generating the URL from Visual Studio using the AWS plugin generates a working URL that looks like this (again formatted and sanitized):

https://{bucket}.s3.amazonaws.com/{key}?
    X-Amz-Expires=900
    &X-Amz-Algorithm=AWS4-HMAC-SHA256
    &X-Amz-Credential={access key}/20150626/us-east-1/s3/aws4_request
    &X-Amz-Date=20150626T195148Z
    &X-Amz-SignedHeaders=host
    &X-Amz-Signature={signature}

What am I doing wrong that my pre-signed URLs are not being generated properly for downloading aws:kms encrypted files?


Solution

  • Alright, after much digging I found where I went wrong. To use SignatureVersion4, you need to set that before the client is created via a static property on the AWSConfigs.S3Config object. All the other fields I set are not needed in my case either.

    Thus the proper way to do it is:

     private static AmazonS3Client CreateClient()
     {
         AWSConfigs.S3Config.UseSignatureVersion4 = true;
         return new AmazonS3Client(RegionEndpoint.USEast1);
     }
    

    And creating the request

     var request = new GetPreSignedUrlRequest
     {
         BucketName = bucket,
         Key = key,
         Expires = expires
     };