使用 OAuth 1.0 生成签名时出现问题

Posted

技术标签:

【中文标题】使用 OAuth 1.0 生成签名时出现问题【英文标题】:Problem generating signature with OAuth 1.0 【发布时间】:2021-06-10 11:18:43 【问题描述】:

我在 powershell 中生成有效的 HMAC-SHA1 签名时遇到问题,我最终希望将其与 Flickr API 一起使用。我已经坚持了几个星期,所以如果有人请告诉我我做错了什么,那将不胜感激。谢谢你:)

这是我写的代码:

$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

当签名被发送到 Flickr 时,服务器拒绝它,声明“oauth_problem=signature_invalid”。

我错过了一步吗?

谢谢。

【问题讨论】:

【参考方案1】:

看看Flickr's docs中的示例payload:

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

现在与您的脚本生成的有效负载字符串 ($SignatureBase) 进行比较:

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

这告诉我们什么?好吧,您似乎过度转义正在签名的有效负载。

改变这个:

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

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


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

只是:

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

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

现在剩下的就是通过更改这一行来逃避生成的签名:

$SignatureParams += @'oauth_signature'=$oauth_Signature       

到:

$SignatureParams += @ 'oauth_signature' = [uri]::EscapeDataString($oauth_Signature) 

【讨论】:

谢谢,这两种方法我都试过了。即使将字符串设置为“GET&https%3A%2F%2Fwww.flickr.com%2Fservices...”,正在计算的签名仍然被拒绝 @Lee 您仍然需要转义最终签名(我已经更新了答案) 谢谢。使用实时 api 密钥,在进行上述更改后,转义签名现在看起来像 ...&oauth_signature=47jBUIguxrrjJYVk%2BtQbhuv79TY%3D&oauth_signature_method=HMAC-SHA1...。不幸的是,flickr 仍然不喜欢它。 @Lee 那我恐怕没有好主意了:-) 是的,我也是。感谢您的帮助。【参考方案2】:

我同意马蒂亚斯的观点。

您最初在签名库中转义 url,然后在将所有元素连接到签名库之前转义所有元素。

然后在这里,你转义了整个构造的字符串:

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

这是用百分比转义码 (%25) 替换每个先前转义字符的百分比符号。

省略上面提到的代码行。正如文档所述,每个元素都应该在与 & 号连接之前进行转义。

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

另外,我会将 $signatureParams 哈希表转换为字符串数组,然后转换为 -join '&'。这样就无需稍后去除尾随编码的“&”。

我认为您不需要对 sigKey 进行 UTF8 编码。你的 ::EscapeDataString() 方法应该可以处理这个问题。

请注意,在您对 $ouath_signature 进行 base64 编码后,必须先对其进行转义(URL 编码),然后再将其分配给 AuthZ 标头中的“oauth_signature”参数。

我建议使用 Postman 测试您的授权,并比较生成的签名。

https://www.postman.com/

他们有一个关于如何使用 Postman 的 OAuth 1.0 文档:https://learning.postman.com/docs/sending-requests/authorization/#oauth-10

【讨论】:

以上是关于使用 OAuth 1.0 生成签名时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 OAuth2 服务帐户生成访问令牌时出现问题

OAuth 1.0 在 POSTMAN 和 PHP 代码中生成签名差异

生成签名的 apk 时出现 TaskExecutionException

使用预签名 URL 将文件上传到 Amazon S3 时出现 CORS 错误

生成 OAuth 2 SASL 字符串时出现错误代码 400

Flutter Web,向具有自签名证书的服务器发出请求时出现问题