我应该将密钥对象的哪一部分传递给' openssl_verify '来验证Google签名的JWT ?


What part of the key object do I pass into `openssl_verify` to verify a Google signed JWT?

我正在尝试使用Google和OpenID Connect实现联邦登录系统,并且我无法验证和解析从Google返回的JWT id令牌。我在这里遵循谷歌的文档。

根据文档的建议,我尝试使用现有的JWT库。GitHub上最流行的PHP版本似乎是PHP_JWT。问题似乎出在JWK密钥的格式上。

上面链接的Google文档说要从jwks_uri端点获取密钥,如其发现文档中所示。该端点返回以下内容:

{
 "keys": [
  {
   "kty": "RSA",
   "alg": "RS256",
   "use": "sig",
   "kid": "1771931eb0eb64eb97733e857685be153e079bb9",
   "n": "AMNFQMNJw/EVwrYsyPTnEHWkaPinPb4ngc/SqD701aisFhbU9/wWoKADeFtwfBcWl1qjzIqhPorQElB+2mtiqUh3Qtaazt1x5wA9XnJDe6kjtMGm9nNLMilSVNBilAE8GIdbciMycISfOfL0WRaJrqpNxewNEVZjuYiGzOWahiDP",
   "e": "AQAB"
  },
  {
   "kty": "RSA",
   "alg": "RS256",
   "use": "sig",
   "kid": "7b3bc600209875d3c42ae277a0d018d1d21986ec",
   "n": "AN2UvG5+hNEMIPIbnpPm+JQi6LFWXBPzg3Ltb3xkVmSTjVaCFWppw/ZYRBgpToGKZP9XJstlOE88SDUFSMZIkIqtLpnUqmZax2Zc2gjEB9PhmHSH3/tTmtZ1U0X6V+crqitZ2uc3NV78vCn9/s+WuPwk/gfKBG8Cirb0fgLmsPd9",
   "e": "AQAB"
  }
 ]
}

查看JWT类的decodeverify方法的源代码,似乎$keys参数可以是一个数组,但它们期望数组键为kid,数组值为:@param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key。拔出kid属性并将它们用作数组键很简单,但是应该使用什么作为数组值呢?

从谷歌的JWKs文档中,看起来我们正在使用RS*,但我不知道关键对象的哪一部分是resource of an openssl public key。我试过使用整个stdClass对象和n字符串,但在openssl_verify步骤都失败了。该函数发出一个通知:"警告:openssl_verify():提供的密钥参数不能被强制转换为公钥"。

显然,我传入了错误的键,但正确的键是什么呢?

Google的库似乎使用不同的端点来获取密钥。该端点似乎返回一个证书数组。我需要用类似的东西来代替吗?如果是这样,为什么文档会告诉您使用jwks_uri端点?

对于将来尝试这样做的人,我想提供一个完整的答案;

jwks_uri JSON密钥中的ne部分给出了模数和指数,可用于检索公钥(这是验证签名所需的全部)。这篇文章详细介绍了如何在纯PHP中完成:

openssl:如何从模数

中获取公钥

然而,你应该意识到,在谷歌的jwks_uri提供的JSON文档使用URL安全base64编码(即普通base64 +和/字符替换)。如果不考虑这一点,可能仍然会给您一个无效的证书。由于您提到使用php-jwt,使用phpseclib从模数和指数中获取公钥的工作代码是:

$modulus = 'someencodedmodulusvalue';
$exponent = 'someencodedexponentvalue'; 
$rsa = new Crypt_RSA();
$modulus = new Math_BigInteger(JWT::urlsafeB64Decode($modulus), 256);
$exponent = new Math_BigInteger(JWT::urlsafeB64Decode($exponent), 256);
$rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
$rsa->setPublicKey();
$pubKey = $rsa->getPublicKey();

两个端点包含相同的信息,但格式不同。

jwks_uri端点给出了RSA公钥的指数值。您可以使用这两个值生成您在https://www.googleapis.com/oauth2/v1/certs上获得的PEM文件。