我已经为这个工作太久了。我正在寻找一个工作示例,截至2016年9月验证谷歌idToken,如
<>以前eyJhbGciOiJSUzI1NiIsImtpZCI6IjZjNzgxOTQyZDg0OWJhMmVjZGE4Y2VkYjcyZDM0MzU3ZmM5NWIzMjcifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhdWQiOiIxMDQ5MTQ4MTU2NTQ2LTk2YjFxcTJsNTJtODVtODB0ZHVoZHVma2RwODRtN2tuLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEyNTk4NDgzNjQ2MjY1OTYxNTQwIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjEwNDkxNDgxNTY1NDYtdjJwZjRlbGhzOGNwcXBlcWZkMzU5am5nOWs5aW5kcTQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJlbWFpbCI6InRlc3R1c2VydGh4QGdtYWlsLmNvbSIsImlhdCI6MTQ3NDc1NDMzMiwiZXhwIjoxNDc0NzU3OTMyLCJuYW1lIjoiVGVzdCBVc2VyIiwicGljdHVyZSI6Imh0dHBzOi8vbGg0Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tU0dldkZZRDlaWFEvQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQVBhWEhoUmtuX1hEaEhNLTEzeVMwTUtBcFNrZG1zVEdYdy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiVGVzdCIsImZhbWlseV9uYW1lIjoiVXNlciIsImxvY2FsZSI6ImVuIn0.btukbBvhek6w14CrBVTGs8X9_IXIHZKpV1NzJ3OgbGUfmoRMirNGzZiFAgrR7COTeDJTamxRzojxxmXx6EEkQqNQcbyN8dO0PTuNt9pujQjLbFw_HBhIFJQaJSR3-tYPN-UtHGQ5JAAySsvCPapXbxyiKzTyvGYRSU65LmyNuiGxe6RQe1zHjq2ABJ4IPRqKPuFupnGRPWYyBSTPU7XQvtfhgyqA0BWZUfmCIFyDxQhvMaXNLTs01gnGVhcUDWZLi9vuUiKUlz3-aSSbwdfCMAljhBHnjpYO6341k5-qmgKkWawv8DX_nMEzntsCMCr664rP4wFEbsRB5ledM9Pc9Q 之前使用谷歌推荐的方式,为jwks_uri提取"accounts.google.com/.well-known/openid-configuration",并提取"www.googleapis.com/oauth2/v3/certs",生成
的相关条目{:"kty RSA",:"alg RS256","使用":"团体","孩子":"6 c781942d849ba2ecda8cedb72d34357fc95b327","n":"s1dt5wFFaYl-Bt7Yb7QgWEatLJfxwWDhbd5yvm2Z4d1PRgNVQa9kwOArQNoOJ-b-oZnXLVFsVASUXEAumGf1ip5TVCQmMBKqlchSDNuoZfoWdpCCX7jx4gNuS43pS6VqV3QDjWnoXRTHaUi5pZEbpAmWpOeG_CfmewNVwBXPFx8-mtvEdtxIrspX4ayXTViR4vHc7MhQhUxllFbocxMjJysDQuZV9wN3MI0lVtQdf52SKJwF3lhvWA9-WAEZ1q8wq-I93Sfte95RaFjDqCH——Sh-8DjhK4OvgItcEGd5QRHjdLvrayPwaDQbpMRN2n3BkVWIxKJubtRiSeWbawCklQ","e":"AQAB"} 之前验证发生,如果我传递令牌https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=TOKEN,但这不是一个真正的答案,因为他们不经常改变,但每次做一个额外的web调用只是自找麻烦。
谁能给我举个例子?我试过phpseclib,但它从来没有验证过。到现在为止,我大概已经找了40个小时了,我已经无计可举了。
任何帮助都是感激的。
我的相关代码:
输出$modulus = ""; $exponent = ""; $token = $_POST['token']; $pieces = explode(".", $token); $header = json_decode(base64_decode(str_replace(['-','_'], ['+','/'], $pieces[0])), true); $alg = $header['alg']; $kid = $header['kid']; $payload = base64_decode(str_replace(['-','_'], ['+','/'], $pieces[1])); $signature = str_replace(['-','_'], ['+','/'], $pieces[2]); //$signature = base64_decode(str_replace(['-','_'], ['+','/'], $pieces[2])); if (testGoogleList($alg, $kid, $modulus, $exponent)) { echo "Found in list: kid=".$kid."n"; echo "n: (base64URL)".$modulus."n"; echo "e: (base64URL)".$exponent."n"; $modulus = str_replace(['-','_'], ['+','/'], $modulus); $exponent = str_replace(['-','_'], ['+','/'], $exponent); echo "n: (base64)".$modulus."n"; echo "e: (base64)".$exponent."n"; $rsa = new Crypt_RSA(); $rsa->setHash("sha256"); $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); $modulus = new Math_BigInteger($modulus, 256); $exponent = new Math_BigInteger($exponent, 256); echo "n: (BigInteger)".$modulus."n"; echo "e: (BigInteger)".$exponent."n"; $rsa->loadKey(array('n' => $modulus, 'e' => $exponent)); $rsa->setPublicKey(); $pubKey = $rsa->getPublicKey(); echo "Public Key from phpseclibn".$pubKey."n"; echo "--First openSSL error check--n"; while ($msg = openssl_error_string()) echo $msg . "<br />n"; echo "--After First Error Check, before Verify--n"; $res = $rsa->verify($pieces[0].".".$pieces[1], $signature); while ($msg = openssl_error_string()) echo $msg . "<br />n"; echo "--Verify result: ".var_export($res, true)."--n"; }
在列表中找到:kid=6c781942d849ba2ecda8cedb72d34357fc95b327n: (base64URL) s1dt5wFFaYl-Bt7Yb7QgWEatLJfxwWDhbd5yvm2Z4d1PRgNVQa9kwOArQNoOJ-b-oZnXLVFsVASUXEAumGf1ip5TVCQmMBKqlchSDNuoZfoWdpCCX7jx4gNuS43pS6VqV3QDjWnoXRTHaUi5pZEbpAmWpOeG_CfmewNVwBXPFx8-mtvEdtxIrspX4ayXTViR4vHc7MhQhUxllFbocxMjJysDQuZV9wN3MI0lVtQdf52SKJwF3lhvWA9-WAEZ1q8wq-I93Sfte95RaFjDqCH——Sh-8DjhK4OvgItcEGd5QRHjdLvrayPwaDQbpMRN2n3BkVWIxKJubtRiSeWbawCklQ艾凡:AQAB (base64URL)护士:(base64) s1dt5wFFaYl + Bt7Yb7QgWEatLJfxwWDhbd5yvm2Z4d1PRgNVQa9kwOArQNoOJ + b + oZnXLVFsVASUXEAumGf1ip5TVCQmMBKqlchSDNuoZfoWdpCCX7jx4gNuS43pS6VqV3QDjWnoXRTHaUi5pZEbpAmWpOeG/CfmewNVwBXPFx8 + mtvEdtxIrspX4ayXTViR4vHc7MhQhUxllFbocxMjJysDQuZV9wN3MI0lVtQdf52SKJwF3lhvWA9 + WAEZ1q8wq + I93Sfte95RaFjDqCH + + Sh + 8 djhk4ovgitcegd5qrhjdlvraypwadqbpmrn2n3bkvwixkjubtrisewbawcklq艾凡:AQAB base64护士:先导入BigInteger) (18674717054764783973087488855176842456138281065703345249166514684640666364313492818979675328236363014396820758462507776710767978395332237045824933690552916871072924852353561300648679961653291310130667565640227949181785672954620248276915721938277908962537175894062430220752771265500386404609948390377043762106166027544443459977210114747088393335234720657330424186435226141073425445733987857419933850994487913462193466159335385639996611717486282518255208499657362420183528330692236194252505592468150318350852955051377118157817611947817677975817359347998935961426571802421142861030565807099600656362069178972477827638867161671399657071319083914500667014214521757304661303525496653078786180348831678824969667950119891369610525474165187687495455755684504105433077872587114630537058768184460798470456362909589578101896361255070801艾凡:先导入BigInteger) (1095844162phpseclib中的公钥-----开始公钥-----MIIBeDANBgkqhkiG9w0BAQEFAAOCAWUAMIIBYAKCAVZzMWR0NXdGRmFZbCtCdDdZYjdRZ1dFYXRMSmZ4d1dEaGJkNXl2bTJaNGQxUFJnTlZRYTlrd09BclFOb09KK2Irb1puWExWRnNWQVNVWEVBdW1HZjFpcDVUVkNRbU1CS3FsY2hTRE51b1pmb1dkcENDWDdqeDRnTnVTNDNwUzZWcVYzUURqV25vWFJUSGFVaTVwWkVicEFtV3BPZUcvQ2ZtZXdOVndCWFBGeDgrbXR2RWR0eElyc3BYNGF5WFRWaVI0dkhjN01oUWhVeGxsRmJvY3hNakp5c0RRdVpWOXdOM01JMGxWdFFkZjUyU0tKd0YzbGh2V0E5K1dBRVoxcTh3cStJOTNTZnRlOTVSYUZqRHFDSCsrU2grOERqaEs0T3ZnSXRjRUdkNVFSSGpkTHZyYXlQd2FEUWJwTVJOMm4zQmtWV0l4S0p1YnRSaVNlV2Jhd0NrbFECBEFRQUI =----- end公钥-----第一次openSSL错误检查——在第一次错误检查之后,在验证之前——错误:0906D06C:PEM例程:PEM_read_bio:no start line
——验证结果:false——
对于从搜索引擎来到这里的人:
我正在尝试使用Google ID令牌来验证我的登录凭据是:
准确的
不欺骗
能够被后端服务器检查
使用数学计算,所以我不必每次都查询谷歌(添加延迟和if-anything-can-go- error -it-will effect)
我知道大多数人肯定能读懂这段代码,但我想把它打出来,为下一个愤怒的灵魂解释一下发生了什么。
你的第一部分可能会有所不同,因为我来自Android,所以它相当直接。
我的过程是在Android中请求令牌。(仅与所示示例和相关片段不同)
GoogleSignInOptions gso = new GoogleSignInOptions. builder (GoogleSignInOptions. default_sign_in).requestIdToken (getString (R.string.client_id)).requestEmail ().build ();之前从活动结果(onActivityResult)获取令牌
GoogleSignInAccount account = result.getSignInAccount();String idToken = acct.getIdToken();该令牌由3个部分组成,以句号分隔,格式为"$header.$info.$signature"。我们将验证"$header"。$info"使用"$signature"。
$报头包含有关加密的信息,例如(解码后):
{"alg":"RS256"、"孩子":"6 c781942d849ba2ecda8cedb72d34357fc95b327"}之前所以使用的算法是"SHA-256, with RSA Encryption",密钥库中的Key ID为6c781942d849ba2ecda8cedb72d34357fc95b327。我们将在以后使用它。
通过HTTP
将整个令牌传递给我的后端服务器然后使用以下代码解码令牌,直接取自接受的答案
include('Crypt/RSA.php'); //path to phpseclib $modulus = ""; $exponent = ""; $token = $_POST['token']; $pieces = explode(".", $token); $data = $pieces[0].".".$pieces[1]; $signature = base64_decode(str_replace(['-','_'], ['+','/'], $pieces[2])); $header = json_decode(base64_decode(str_replace(['-','_'], ['+','/'], $pieces[0])), true); $alg = $header['alg']; $kid = $header['kid']; if (testGoogleList($alg, $kid, $modulus, $exponent)) { $modulus = base64_decode(str_replace(['-','_'], ['+','/'], $modulus)); $exponent = base64_decode(str_replace(['-','_'], ['+','/'], $exponent)); $rsa = new Crypt_RSA(); $rsa->loadKey([ 'n' => new Math_BigInteger($modulus, 256), 'e' => new Math_BigInteger($exponent, 256) ]); $rsa->setHash('sha256'); $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); if ($rsa->verify($data, $signature)) { echo "VALID!!!!"; } else { echo "NOT VALID :'("; } }
我们做
base64_decode(str_replace(['-','_'], ['+','/'], $VARIABLE))
的原因是因为它们以base64URL
的形式呈现,其中"+"改为"-","/"改为"_"。所以我们把它从base64URL> base64>未编码的(纯)文本。这是做什么的?
- 我们从$_POST中获取token(我称之为$token)。 然后我们把它分成几个部分。
- 请记住,我们需要使用第三部分来解码前两个对,中间用句点("。")分隔。("$signature"是"$header.$info"的加密签名)
- 完全解码签名,从base64URL到未编码(纯)文本。由于Google使用JSON来存储密钥信息,json_decode报头并获得加密类型和密钥id。
我把它包装在一个函数中,但我的函数
testGoogleList
基本上是这样工作的:- 所以我们传入算法和密钥id。
- 我测试我的本地密钥缓存,看看我们需要的密钥是否已经缓存。
如果没有,我们继续这里,否则跳到第4步。
- 然后我们点击网页,在(https://accounts.google.com/.well-known/openid-configuration)使用get_file_contents()或CURL方法抓取Google的open-id配置页面,如果你不能。我不得不使用CURL选项"curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);"在我的CURL方法,因为它没有正确地尝试HTTPS。
- 该页面是JSON编码的文本文件,所以json_decode它。
- 我们抓取"jwks_uri"键并抓取该页面,就像我们上面所做的那样。
- 这包含了一组Google当前用于公钥验证的密钥。我json_decode并临时存储到一个数组。
- 截断旧缓存并重写集合。不要忘记使用flock(),以防时机真的很差。
- 确保您的密钥在新集合中。
如果我们在缓存中找到键,我们从中提取"n"(我们称之为"模数")和"e"("指数")片段并将它们传递回去。
然后从base64URL> base64>未加密(明文)文本中解码模数和指数块。
- 创建一个新的Crypt_RSA类实例
- 将您刚刚解密的数字作为新密钥加载到该类中,使用Math_BigInteger类型,以便我们可以对大数字进行数学运算。(第二个参数是基数,所以基数256是一个字节,如果我们使用大整数,使用这个)
- 设置我们的哈希和签名模式,以匹配我们从谷歌得到的。
- 执行验证以确保我们有一个有效的密钥。
这之后就看你怎么用了。
再次感谢neubert的帮助!
问题是您没有base64解码任何相关内容。
这对我有效(告诉我签名是有效的):
<?php
include('Crypt/RSA.php');
$data = 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjZjNzgxOTQyZDg0OWJhMmVjZGE4Y2VkYjcyZDM0MzU3ZmM5NWIzMjcifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhdWQiOiIxMDQ5MTQ4MTU2NTQ2LTk2YjFxcTJsNTJtODVtODB0ZHVoZHVma2RwODRtN2tuLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEyNTk4NDgzNjQ2MjY1OTYxNTQwIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjEwNDkxNDgxNTY1NDYtdjJwZjRlbGhzOGNwcXBlcWZkMzU5am5nOWs5aW5kcTQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJlbWFpbCI6InRlc3R1c2VydGh4QGdtYWlsLmNvbSIsImlhdCI6MTQ3NDc1NDMzMiwiZXhwIjoxNDc0NzU3OTMyLCJuYW1lIjoiVGVzdCBVc2VyIiwicGljdHVyZSI6Imh0dHBzOi8vbGg0Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tU0dldkZZRDlaWFEvQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQVBhWEhoUmtuX1hEaEhNLTEzeVMwTUtBcFNrZG1zVEdYdy9zOTYtYy9waG90by5qcGciLCJnaXZlbl9uYW1lIjoiVGVzdCIsImZhbWlseV9uYW1lIjoiVXNlciIsImxvY2FsZSI6ImVuIn0';
$signature = 'btukbBvhek6w14CrBVTGs8X9_IXIHZKpV1NzJ3OgbGUfmoRMirNGzZiFAgrR7COTeDJTamxRzojxxmXx6EEkQqNQcbyN8dO0PTuNt9pujQjLbFw_HBhIFJQaJSR3-tYPN-UtHGQ5JAAySsvCPapXbxyiKzTyvGYRSU65LmyNuiGxe6RQe1zHjq2ABJ4IPRqKPuFupnGRPWYyBSTPU7XQvtfhgyqA0BWZUfmCIFyDxQhvMaXNLTs01gnGVhcUDWZLi9vuUiKUlz3-aSSbwdfCMAljhBHnjpYO6341k5-qmgKkWawv8DX_nMEzntsCMCr664rP4wFEbsRB5ledM9Pc9Q';
$signature = str_replace(['-','_'], ['+','/'], $signature);
$signature = base64_decode($signature);
$n = 's1dt5wFFaYl-Bt7Yb7QgWEatLJfxwWDhbd5yvm2Z4d1PRgNVQa9kwOArQNoOJ-b-oZnXLVFsVASUXEAumGf1ip5TVCQmMBKqlchSDNuoZfoWdpCCX7jx4gNuS43pS6VqV3QDjWnoXRTHaUi5pZEbpAmWpOeG_CfmewNVwBXPFx8-mtvEdtxIrspX4ayXTViR4vHc7MhQhUxllFbocxMjJysDQuZV9wN3MI0lVtQdf52SKJwF3lhvWA9-WAEZ1q8wq-I93Sfte95RaFjDqCH--Sh-8DjhK4OvgItcEGd5QRHjdLvrayPwaDQbpMRN2n3BkVWIxKJubtRiSeWbawCklQ';
$n = str_replace(['-','_'], ['+','/'], $n);
$n = base64_decode($n);
$e = 'AQAB';
$e = base64_decode($e);
$rsa = new Crypt_RSA();
$rsa->loadKey([
'n' => new Math_BigInteger($n, 256),
'e' => new Math_BigInteger($e, 256)
]);
$rsa->setHash('sha256');
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
echo $rsa->verify($data, $signature) ?
'valid' :
'invalid';