尝试使用 Google Drive API 的错误 JWT 签名

Posted

技术标签:

【中文标题】尝试使用 Google Drive API 的错误 JWT 签名【英文标题】:Bad JWT signature trying to use the Google Drive API 【发布时间】:2017-10-12 04:09:59 【问题描述】:

我看到过有关使用 PowerShell 中的个人 OAuth2 令牌使用 Google Drive 操作文件的文章,但没有使用域范围的权限。

据我了解,工作流程是这样的:

    创建标头,base64 编码 创建声明,base64 编码 结合标头和声明,然后使用 RSA256 和 Google 提供的私钥对其进行签名

我在第 3 步遇到问题。请参阅以下代码:

$firstdate = Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0
$endtime = (New-TimeSpan -Start $firstdate -End (Get-Date).AddMinutes(30).ToUniversalTime()).TotalSeconds
$issuetime = (New-TimeSpan -Start $firstdate -End (Get-Date).ToUniversalTime()).TotalSeconds
$jsonfile = Get-Content 'secret_file.json' | ConvertFrom-Json

$headerhash = [ordered]@
    'alg' = 'RS256'
    'typ' = 'JWT'

$headerjson = $headerhash | ConvertTo-Json -Compress
$encodedheader = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson))

$claimhash = [ordered]@
    'iss' = $jsonfile.client_email
    'scope' = 'https://www.googleapis.com/auth/drive'
    'aud' = 'https://www.googleapis.com/oauth2/v4/token'
    'exp' = [math]::Round($endtime,0)
    'iat' = [math]::Round($issuetime,0)

$claimjson = $claimhash | ConvertTo-Json -Compress
$encodedclaim = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($claimjson))

#$sha = [System.Security.Cryptography.RSACryptoServiceProvider]::Create()
#$sha.Key = [System.Text.Encoding]::UTF8.Getbytes($jsonfile.private_key)
#$sha.FromXmlString($sha.ToXmlString($jsonfile.private_key))
#$signature = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("$encodedheader.$encodedclaim"))
#$signature = $sha.SignData([System.Text.Encoding]::UTF8.GetBytes("$encodedheader.$encodedclaim"),[System.Security.Cryptography.CryptoConfig]::MapNameToOID('RSASSA-PKCS1-V1_5-SIGN'))
#$encodedsignature = [System.Convert]::ToBase64String($signature)
#$formatter = [System.Security.Cryptography.RSAPKCS1SignatureFormatter]::new($sha)
#$formatter.SetKey($sha)
#$formatter.CreateSignature([System.Text.Encoding]::UTF8.GetBytes("$encodedheader.$encodedclaim"))

$jws = "$encodedheader.$encodedclaim"
$encodedjws = [System.Text.Encoding]::UTF8.GetBytes($jws)

$rsa = [System.Security.Cryptography.RSACryptoServiceProvider]::Create()
# This is the key -- need to convert PEM to proper format
$rsa.FromXmlString($rsa.ToXmlString($jsonfile.private_key))
$sha256OID = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256")
$signature = $rsa.SignData($encodedjws,$sha256OID)
$encodedsignature = [System.Convert]::ToBase64String($signature)

$jwt = "$encodedheader.$encodedclaim.$encodedsignature"

$requestUri = 'https://www.googleapis.com/oauth2/v4/token'
$method = 'POST'
$body = [ordered]@
    'grant_type' = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
    'assertion' = $jwt

Invoke-webrequest -Uri $requestUri -Method $method -Body $body -ContentType application/x-www-form-urlencoded

我遇到的错误表明 JWT 签名错误。 Google 提供的密钥是 PEM 格式,但我在将其转换为 Powershell 可以理解的内容时遇到了问题。

【问题讨论】:

我认为这是 Powershell 的“王牌”问题,想知道您是否最终想通了..?我摆弄了非常相似的代码来尝试在此处使用 HTTP/REST 实现 Google 的 OAuth 2.0:developers.google.com/identity/protocols/OAuth2ServiceAccount 不能使用 UrlEncode,所以请改用干净 $encodedheader = $encodedheader.Split('=')[0] $encodedheader = $encodedheader.Replace('+', '-') $encodedheader = $encodedheader.Replace('/', '_') $encodedclaim 和 $encodedsignature 相同 你怎么知道这个调用是否正确返回了私钥..? $rsa.FromXmlString($rsa.ToXmlString($jsonfile.private_key));肯定会得到一个令人印象深刻的 $encodedsignature 回来,虽然它看起来有点短..!你怎么知道它是否真的正确..? 【参考方案1】:

我有一段时间遇到同样的问题,直到我遇到使用 OWIN 库来解决它。

这是我的 Get-GSToken 函数的链接,它是我的 PSGSuite 模块的一部分。如果你想做你需要的事情,你可以把它拆开(该函数的目的是获取一个令牌),但你还需要 nuget 文件夹才能让它工作(OWIN 库是包含在里面):

https://github.com/nferrell/PSGSuite/blob/master/Public/Get-GSToken.ps1

随时获取整个模块的示例!我已经将几乎所有的 Google API 调用都封装到其中的 Powershell 函数中,所有这些都利用 Get-GSToken 首先在函数需要的范围内获取令牌。

需要注意的是:这利用了服务帐户和 P12 密钥文件,而不是 clientsecrets.json 文件密钥类型。我不确定如何使用 JSON 文件使用纯 Powershell 构建可工作的 JWT,但 P12 + OWIN 库非常适合它。

【讨论】:

看起来不错,@nferrell,谢谢。您在该函数中使用这些 OWIN 库的唯一目的是进行 base64 编码。我不确定模块的其余部分在哪里使用它,但您可以轻松地使用 [System.Convert]::ToBase64String() 。不过,我认为我们可以认为这已解决。 OWIN 库专门处理 WebSafe/URLSafe Base64 编码,没有真正的 ($encoder = New-Object Microsoft.Owin.Security.DataHandler.Encoder.Base64UrlTextEncoder) 内置库...我尝试通过字符替换来解决并考虑填充,但在我研究它的几个月里它从未起作用。如果我知道它可以快速简单地使用,我个人完全可以使用外部库

以上是关于尝试使用 Google Drive API 的错误 JWT 签名的主要内容,如果未能解决你的问题,请参考以下文章

使用服务帐户的Google Drive API问题

Google Drive API 推送通知订阅 400“解析错误”

Android API Google Drive“连接失败”

XMLHttpRequest Access-Control-Allow-Origin 错误 Google Drive API

cron 运行网站备份 google drive api 脚本时出现“无效授权”错误

Drive Builder错误:API Google Drive Spring Boot