Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate AWS S3 pre-signed URL using signature version 4

I am generating a pre-signed URL in a C# .NET Core 2.0 API controller class using the AWSSDK.S3 (3.3.31.11). The resulting URL is intended to be used by a client side Angular application to upload a file to an S3 bucket that is encrypted using SSE-KMS. Although the S3Client reports that the SignatureMethod is "HmacSHA256" and SignatureVersion is "4", when I try to upload a file using the pre-signed URL I get an error indicating "Requests specifying Server Side Encryption with AWS KMS managed keys require AWS Signature Version 4."

The S3Client is being injected as a dependency to the controller class. Instantiation of the object is managed by .NET Core DI framework using configuration settings in a config.json file:

{
    ...
    "AWS": {
        "Profile": "default",
        "Region": "us-east-1"
    }
    ...
}

I am testing the pre-signed URL using curl:

curl -H "Content-Type: application/pdf" -H "x-amz-server-side-encryption: aws:kms" -H "x-amz-server-side-encryption-aws-kms-key-id: {kms-key-id}" -k -T "filename.pdf" "https://mybucketname.s3.amazonaws.com/filename.pdf?AWSAccessKeyId={keyid}&Expires={expires}&x-amz-security-token={token}&Signature={signature}"

I've discovered that if I don't include the "Content-Type" header I receive a "SignatureDoesNotMatch" error code, rather than the "InvalidArgument" error.

This process had been working previously when using default AES-256 encryption for the S3 bucket, it is only in converting to SSE-KMS that I have encountered this problem.

// Controller Class
private readonly IAmazonS3 _s3Client;

public MyController(IAmazonS3 s3Client)
{
    _s3Client = s3Client;
}

[HttpPost]
public async Task<IActionResult> GetPreSignedUrl([FromBody] FileInfoDto fileInfo)
{
    ...
    GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
    {
        BucketName = bucketName, 
        Key = fileInfo.name, 
        Verb = HttpVerb.PUT,
        ContentType = fileInfo.contentType, 
        Expires = DateTime.Now.AddMinutes(5),
        ServerSideEncryptionKeyManagementServiceKeyId = keyId, 
        ServerSideEncryptionMethod = ServerSideEncryptionMethod.AWSKMS
    };

    try
    {
        url = _s3Client.GetPreSignedURL(request);
    }
    ...
}

// Startup Class
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddDefaultAWSOptions(_config.GetAWSOptions());
    services.AddAWSService<IAmazonS3>();
    ...
}

When uploading the file using curl and the pre-signed URL that was generated by the controller method, I am getting a response error code of "InvalidArgument" and message "Requests specifying Server Side Encryption with AWS KMS managed keys require AWS Signature Version 4." However, the S3Client reports that SignatureVersion is "4".

What should I be doing differently in order to generate the pre-signed URL using signature version 4?

like image 927
Robert Lysik Avatar asked Jan 25 '19 16:01

Robert Lysik


1 Answers

Despite the fact that the s3Client reports Signature Version as "4", the following line, added to the ConfigureServices method, resolves the issue and results in generating a pre-signed URL conforming to Signature Version 4:

AWSConfigsS3.UseSignatureVersion4 = true;

The pre-signed URL that is generated is as follows:

https://mybucketname.s3.amazonaws.com/filename.pdf?X-Amz-Expires=1800&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential={credential}/20190127/us-east-1/s3/aws4_request&X-Amz-Date={date}&X-Amz-SignedHeaders=content-type;host;x-amz-server-side-encryption&X-Amz-Signature={signature}
like image 69
Robert Lysik Avatar answered Nov 14 '22 23:11

Robert Lysik