0

I am having a problem generating a valid HMAC-SHA1 signature in powershell which I eventually would like to use with the Flickr API. I have been stuck on this for a couple of weeks, so if somebody please tell me what I a doing wrong, that would be much appreciated. Thank you :)

Here is the code I have written:

$oauth_signature_method = 'HMAC-SHA1'
[string]$oauth_nonce    = New-Guid   
[int]$oauth_timestamp   = [int](Get-Date -UFormat %s -Millisecond 0) 
$oauth_version          = '1.0' 
$oauth_consumer_key     = 'e1d618f39j69s6a87443b182a1e91084' #NOT REAL KEY
$oauth_consumer_secret  = 'dd6jjff7423acb3a' # NOT REAL KEY
$url                    = 'https://www.flickr.com/services/oauth/request_token'
$method                 = 'GET'
$oauth_callback         = 'oob'
    
$signature_key          = [System.Uri]::EscapeDataString($oauth_consumer_key)+"&"+[System.Uri]::EscapeDataString($oauth_consumer_secret);
$method                 = $method.ToUpper()

$response=""
$oauth_signature = ""

# Build the signature
$SignatureBase = "$([System.Uri]::EscapeDataString($url))&"
            
$SignatureParams = @{
                'oauth_callback'         = [System.Web.HttpUtility]::UrlEncode($oauth_callback);
                'oauth_consumer_key'     = $oauth_consumer_key;
                'oauth_nonce'            = $oauth_nonce.replace("-","");
                'oauth_signature_method' = $oauth_signature_method;
                'oauth_timestamp'        = $oauth_timestamp;
                'oauth_version'          = $oauth_version;
            }   

$SignatureParams.GetEnumerator() | Sort Name |foreach { $SignatureBase += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&") }

$SignatureBase = [System.Uri]::EscapeDataString("$method&")+$SignatureBase.TrimEnd('%26')


$SignatureBase = [System.Uri]::EscapeDataString($SignatureBase)

########################################################################################
# At this point:
# $SignatureBase = GET%2526https%253A%252F%252Fwww.flickr.com%252Fservices%252Foauth%252Frequest_token%26oauth_callback%253Doob%2526oauth_consumer_key%253De1d618f39j69s6a87443b182a1e91084%2526oauth_nonce%253D080a4203ff7e428bb898086dd
13a6697%2526oauth_signature_method%253DHMAC-SHA1%2526oauth_timestamp%253D1615550853%2526oauth_version%253D1.0
# $Signature_key = e1d618f39j69s6a87443b182a1e91084&dd6jjff7423acb3a
########################################################################################

$hmac = New-Object System.Security.Cryptography.HMACSHA1
$hmac.key  = [System.Text.Encoding]::UTF8.GetBytes($Signature_key)

$oauth_signature = $hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($SignatureBase));
$oauth_signature = [System.Convert]::ToBase64String($oauth_signature) 

########################################################################################
# At this point, $oauth_signature = tcKfNZmmRsGhnMmBaIuxRIINJfI=
########################################################################################

#Add the signature into the hashtable   
$SignatureParams += @{'oauth_signature'=$oauth_Signature}       
  
# Reconstruct the URL to include the new signature
$callurl = $url+"?"
$SignatureParams.GetEnumerator() | Sort Name | foreach { $callurl += "$($_.Key)=$($_.Value)&" }
$callurl = $callurl.TrimEnd('&')

########################################################################################
# At this point, this is the URL to get a token:
# https://www.flickr.com/services/oauth/request_token?oauth_callback=oob&oauth_consumer_key=e1d618f39j69s6a87443b182a1e91084&oauth_nonce=080a4203ff7e428bb898086dd13a6697&oauth_signature=tcKfNZmmRsGhnMmBaIuxRIINJfI=&o
auth_signature_method=HMAC-SHA1&oauth_timestamp=1615550853&oauth_version=1.0
########################################################################################

# Send the request to the oAuth server
try {$response = Invoke-RestMethod -uri $callurl -method $method -Headers $APIHeader}

When the signature is sent to Flickr, the server rejects it, stating "oauth_problem=signature_invalid".

Am I missing a step?

Thanks.

Lee
  • 13
  • 3

2 Answers2

0

Take a look at the example payload in Flickr's docs:

GET&https%3A%2F%2Fwww.flickr.com%2Fservices%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252Fwww.example.com%26oauth_consumer_key%3D653e7a6ecc1d528c516cc8f92cf98611%26oauth_nonce%3D95613465%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1305586162%26oauth_version%3D1.0

Now compare with the payload string ($SignatureBase) produced by your script:

GET%2526https%253A%252F%252Fwww.flickr.com%252Fservices%252Foauth%252Frequest_token%26oauth_callback%253Doob%2526oauth_consumer_key%253De1d618f39j69s6a87443b182a1e91084%2526oauth_nonce%253D0e014a368e2845f4860e27e87f6c5d01%2526oauth_signature_method%253DHMAC-SHA1%2526oauth_timestamp%253D1615561837%2526oauth_version%253D1.0

What does that tell us? Well, you appear to be over-escaping the payload being signed.

Change this:

$SignatureParams.GetEnumerator() | Sort Name |foreach { $SignatureBase += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&") }

$SignatureBase = [System.Uri]::EscapeDataString("$method&")+$SignatureBase.TrimEnd('%26')


$SignatureBase = [System.Uri]::EscapeDataString($SignatureBase)

To just:

$SignatureParams.GetEnumerator() | Sort Name |foreach { $SignatureBase += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&") }

$SignatureBase = "$method&" + ($SignatureBase -replace '%26$')

Now all there's left is to escape the resulting signature, by changing this line:

$SignatureParams += @{'oauth_signature'=$oauth_Signature}       

to:

$SignatureParams += @{ 'oauth_signature' = [uri]::EscapeDataString($oauth_Signature) }
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks, I had tried both methods. Even with the string being set to "GET&https%3A%2F%2Fwww.flickr.com%2Fservices...", the signature being calculated is still rejected – Lee Mar 12 '21 at 14:39
  • @Lee You still need to escape the final signature (I've updated my answer) – Mathias R. Jessen Mar 12 '21 at 14:49
  • Thanks. Using live api keys, the escaped signature now looks like `...&oauth_signature=47jBUIguxrrjJYVk%2BtQbhuv79TY%3D&oauth_signature_method=HMAC-SHA1...` after making the changes above. Unfortunately flickr still does not like it. – Lee Mar 12 '21 at 15:02
  • @Lee Then I'm afraid I'm out of good ideas :-) – Mathias R. Jessen Mar 12 '21 at 15:09
  • Yeah, me too. Thanks for your help. – Lee Mar 12 '21 at 15:16
0

I agree with Mathias.

You initially escape the url in the signature base, then escape all elements prior to concatenating them to the signature base.

Then again here, you escape the entire constructed string:

$SignatureBase = [System.Uri]::EscapeDataString($SignatureBase)

This is replacing the percent sign of every previously escaped character with the percent escape code (%25).

Omit the code line mentioned above. As the documentation states each element should be escaped before concatenation with an ampersand.

https://oauth.net/core/1.0a/#anchor13

Also, I would convert the $signatureParams hashtable to a string array, then -join '&'. This removes the need to strip the trailing encoded '&' later.

I do not believe you need to UTF8 encode the sigKey. Your ::EscapeDataString() method should handle that.

On that note, after you base64 encode your $ouath_signature, it must then be escaped (URL Encoded) before assigning it to 'oauth_signature' param in the AuthZ header.

I recommend using Postman to test your authorization, and compare the generated signature.

https://www.postman.com/

They have an OAuth 1.0 document on how to use Postman: https://learning.postman.com/docs/sending-requests/authorization/#oauth-10