'trouble with aws, powershell and the Signature

i want to play with the Amazon Selling Partner API. From Postman everything works fine. From Powershell not.

I tried to derive my powershell script from the documentation https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html and the python example.

I try it all the day, but i do not find the solution.

What i have done:

Whatever i do, i receive error Message Sender SignatureDoesNotMatch

I attached a screenshot from Postman, it shows the response from this endpoint. I know it is "Access denied", it is just really near on the example from amazon. Later on i need to make a sts request to receive temp credentials on each call and i also need to sign every call to the amazon selling partner API.

Maybe someone can help me, please? I'm going nuts :D

Here is my Powershell code from the python example: https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html

I tried both get variants,

  1. Using GET with an authorization header (Python)
  2. Using GET with authentication information in the Query string (Python)

This code is from the 2nd -> Using GET with authentication information in the Query string

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[cultureinfo]::CurrentCulture = 'de-DE'

#powershell variant with example test input from #https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html

<#  
   .Synopsis
     HMACSHA256 signing function used in the construction of a "Signature 4 " request
#>
# translated function from Deriving a signing key using .NET (C#)
function hmacSHA256(
    [string]$data, 
    [byte[]]$key) 
    {
       $hmacsha = New-Object System.Security.Cryptography.HMACSHA256
       $hmacsha.key = $key
       $signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($data))
       
       return $signature
}

<#  
   .Synopsis
    The AWS Signature version 4 creation routine
#>
function getSignatureKey(
    [String]$AWSAccessKey,
    [String]$dateStamp,
    [String]$regionName,
    [String]$serviceName)
{
   [Byte[]]$kSecret    = [System.Text.Encoding]::UTF8.GetBytes("AWS4" + $AWSAccessKey)
   $kDate              = hmacSHA256 -data $dateStamp -key $kSecret
   $kRegion            = hmacSHA256 -data $regionName -key $kDate
   $kService           = hmacSHA256 -data $serviceName -key $kRegion
   $kSigningKey        = hmacSHA256 -data "aws4_request" -key $kService
   
   return $kSigningKey
}

 <#  
   .Synopsis
     Retrieves an SHA hash of a string as required by AWS Signature 4 
#>
function hash($request) {
    $sha256 = new-object -TypeName System.Security.Cryptography.SHA256Managed
    $utf8   = new-object -TypeName System.Text.UTF8Encoding
    $hash   = [System.BitConverter]::ToString($sha256.ComputeHash($utf8.GetBytes($request)))
    return $hash.replace('-','').toLower()
}


# ************* REQUEST VALUES *************

$method = 'GET'
$service = 'iam'
$host1 = 'iam.amazonaws.com'
$region = 'us-east-1'
$endpoint = 'https://iam.amazonaws.com'

$access_key = 'AKIA4KA7FVL7SN2EXAMPLE'
$secret_key = 'EXPL[enter image description here][1]Y59hS5KWKAnfOSnWLjNsiKaK/EXAMPLE'

$now = [DateTime]::UtcNow
$amz_date = $now.ToString('yyyyMMddTHHmmssZ')
$datestamp = $now.ToString('yyyyMMdd')



# ************* TASK 1: CREATE A CANONICAL REQUEST *************
# http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

# Step 2: Create canonical URI--the part of the URI from domain to query 
# string (use '/' if no path)
$canonical_uri = '/' 

# Step 3: Create the canonical headers and signed headers. Header names
# must be trimmed and lowercase, and sorted in code point order from
# low to high. Note trailing \n in canonical_headers.
# signed_headers is the list of headers that are being included
# as part of the signing process. For requests that use query strings,
# only "host" is included in the signed headers.
$canonical_headers = "host:" + $host1 + "`n" 

$signed_headers = "host"

# Match the algorithm to the hashing algorithm you use, either SHA-1 or
# SHA-256 (recommended)
$algorithm = 'AWS4-HMAC-SHA256'
$credential_scope = $datestamp + '/' + $region + '/' + $service + '/' + 'aws4_request'


# Step 4: Create the canonical query string. In this example, request
# parameters are in the query string. Query string values must
# be URL-encoded (space=%20). The parameters must be sorted by name.
$canonical_querystring = "Action=CreateUser&UserName=NewUser&Version=2010-05-08"

$canonical_querystring += "&X-Amz-Algorithm=AWS4-HMAC-SHA256 `n"
$canonical_querystring += "&X-Amz-Credential=" + [uri]::EscapeDataString(($access_key + "/" + $credential_scope))
$canonical_querystring += "&X-Amz-Date=" + $amz_date
$canonical_querystring += "&X-Amz-Expires=30"
$canonical_querystring += "&X-Amz-SignedHeaders=" + $signed_headers

# Step 5: Create payload hash. For GET requests, the payload is an
# empty string ("").
$payload_hash = hash ''

# Step 6: Combine elements to create canonical request
$canonical_request = $method + "`n" + 
                     $canonical_uri + "`n" + 
                     $canonical_querystring + "`n" + 
                     $canonical_headers + "`n" + 
                     $signed_headers + "`n" + 
                     $payload_hash

#$canonical_request_hash = hash -request $canonical_request
#write-host $canonical_request_hash 


# *************************************************************#
# ************* TASK 2: CREATE THE STRING TO SIGN *************#
$string_to_sign = $algorithm + "`n" +  
                  $amz_date + "`n" +  
                  $credential_scope + "`n" + 
                  $canonical_request_hash 


# *************************************************************#
# ************* TASK 3: CALCULATE THE SIGNATURE ***************#
# Create the signing key
$signing_key = getSignatureKey($secret_key, $datestamp, $region, $service)

#write-host "signing-key: $($signing_key)"
# Sign the string_to_sign using the signing_key
$signature = HmacSHA256 -data $string_to_sign -key $signing_key
$signature = [System.BitConverter]::ToString($signature).Replace('-','').ToLower()


# ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
# The auth information can be either in a query string
# value or in a header named Authorization. This code shows how to put
# everything into a query string.

$canonical_querystring += '&X-Amz-Signature=' + $signature

# ************* SEND THE REQUEST *************
# The 'host' header is added automatically by the Python 'request' lib. But it
# must exist as a header in the request.


### 
#I am not sure how to work with headers, because i used the Get Variant with authentication information in the Query string
####
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("authorization", "AWS4-HMAC-SHA256 Credential=$($AWSAccessID)/$($shortdate)/$($AWSRegion)/$($AWSService)/aws4_request, SignedHeaders=$($SignedHeadersList), Signature=$($signature)")


$request_url = $endpoint + "/?" + $canonical_querystring

Invoke-RestMethod  $request_url -Method 'GET' -Headers $headers


Best regards and many many thanks Patrick

Postman screenshot



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source