Hello, I’m trying to write a script that need to perform an authenticated request to AWS S3. As per AWS’s documentation, the request requires to be authenticated via a summary of the request’s method, Date header, bucket name and object key being signed with the secret key using SHA1. I created a PS function that automatically generates the date string with the same format as shown in the documentation and the resulting Authorization header with the signed content. I then tested this using PostMan and it worked as expected.
However, when I’m trying to perform this request using either Invoke-WebRequest or Invoke-RestMethod it fails, claiming that the signatures do not match. Luckily, S3’s response includes the request summary that should be signed to perform the authentication and I found that the date string is being changed from the format I sent to using the UTC time zone and replacing the time zone offset by “GMT”, here is an example of what I mean (some values were redacted for security reasons). The content being printed after the execution of Get-AWSAuthHeaders is the string that is being signed.
>$headers = Get-AWSAuthHeaders GET
GET
Wed, 28 Apr 2021 18:51:53 -0300
/[REDACTED]/clipboard
>$headers
Name Value
---- -----
Date Wed, 28 Apr 2021 18:51:53 -0300
Authorization AWS [REDACTED]:iPvDMgLYHnHVbmnThKHJu7nbuAM=
>try {
>> Invoke-WebRequest -Method GET -Headers $headers -Uri https://[REDACTED].s3-sa-east-1.amazonaws.com/clipboard
>> } catch {
>> $result = $_.Exception.Response.GetResponseStream()
>> $reader = New-Object System.IO.StreamReader($result)
>> $reader.BaseStream.Position = 0
>> $reader.DiscardBufferedData()
>> $responseBody = $reader.ReadToEnd();
>> }
>$responseBody
<?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>[REDACTED]]</AWSAccessKeyId><StringToSign>GET
Wed, 28 Apr 2021 21:51:53 GMT
/[REDACTED]/clipboard</StringToSign><SignatureProvided>iPvDMgLYHnHVbmnThKHJu7nbuAM=</SignatureProvided><StringToSignBytes>[REDACTED]</StringToSignBytes><RequestId>58A6CR4XMSPC8ZCT</RequestId><HostId>fldDRoFiacucacOXVJ93mMVCOhikNKft+01fHuDPKsMz31nhSneVGb96/A23AAn8KE/NAqVV3BY=</HostId></Error>
As you can see, S3 is trying to sign a string with the date in a different format than I’m passing to Invoke-WebRequest and given that this does not happen when using PostMan, means that it’s PowerShell changing the header value on my side and not S3 on AWS’ side.
Here is the code for Get-AWSAuthHeaders in case that it helps (the conf object holds just configuration info for the redacted values):
function Get-AWSAuthHeaders ($method) {
$bucket_name = $conf.bucket_name
$aws_access_key = $conf.aws_access_key
$date_str = (Get-Date).ToString('ddd, d MMM yyyy HH:mm:ss -0300', [CultureInfo]'en-us')
$hmacsha = New-Object System.Security.Cryptography.HMACSHA1
$string_to_sign = "$method`n`n`n$date_str`n/$bucket_name/clipboard"
Write-Host $string_to_sign
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($conf.aws_secret_key)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($string_to_sign))
$signed_b64 = [Convert]::ToBase64String($signature)
return @{
"Authorization" = "AWS $aws_access_key`:$signed_b64"
"Date" = $date_str
}
}
I also tried using PostMan’s feature to export the request as code and it results in the same issue (I also tested exporting it as cURL and it works fine too but I need it using PowerShell, not cURL).