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

Posted

技术标签:

【中文标题】我应该将密钥对象的哪一部分传递给“openssl_verify”以验证 Google 签名的 JWT?【英文标题】:What part of the key object do I pass into `openssl_verify` to verify a Google signed JWT? 【发布时间】:2014-09-25 00:31:39 【问题描述】:

我正在尝试使用 Google 和 OpenID Connect 实现联合登录系统,但我无法验证和解析从 Google 返回的 JWT id 令牌。我在这里关注Google's documentation。

根据文档的建议,我正在尝试使用现有的 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 方法的 the source,似乎 $keys 参数可以是一个数组,但他们希望数组键是 kid 和数组值是:@param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key。拉出kid 属性并将它们用作数组键很简单,但是应该使用什么作为数组值呢?

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

那么,很明显,我输入了错误的键,但正确的键是什么?

Google's library 这似乎使用different endpoint 来获取密钥。该端点似乎返回了一组证书。我需要使用类似的东西吗?如果是这样,为什么文档会告诉您使用 jwks_uri 端点?

【问题讨论】:

【参考方案1】:

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

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

openssl: how can i get public key from modulus

然而,您应该知道 Google 的 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();

【讨论】:

【参考方案2】:

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

jwks_uri 端点为您提供 RSA 公钥的 modulusexponent 值。您可以使用这两个值来生成您在https://www.googleapis.com/oauth2/v1/certs 获得的 PEM 文件。

【讨论】:

谢谢,我还是密码学的新手,所以我没有我需要的词汇。您是否会建议我切换端点或使用可以处理jwks_uri 端点提供的格式的不同 JWT 库? 好吧,我根本不是 php 专家,所以我无法就您应该使用的 JWT 库提出建议。但是我发现了这两个主题:***.com/questions/5973086/… 和***.com/questions/3568012/…。如果您仍想使用 jwks_uri 端点并使用 modulusexponent 值构建 PEM,它们可以为您提供帮助。 再次感谢您的帮助。我找到了一个不错的PHP JWT library,它似乎支持模数和指数键格式,所以我认为改用它是阻力最小的路径。

以上是关于我应该将密钥对象的哪一部分传递给“openssl_verify”以验证 Google 签名的 JWT?的主要内容,如果未能解决你的问题,请参考以下文章

将 api 密钥传递给 rest api

如何将访问密钥和秘密密钥 shell 变量传递给 Redshift 复制命令

在实现 MVC 时,应用程序的哪一部分应该调用 DAO 中的方法? [关闭]

API 密钥和秘密密钥如何工作?如果我必须将我的 API 和密钥传递给另一个应用程序,它会安全吗?

如何使用 process.env 将环境变量中的 API 密钥传递给 Ember CLI?

将部分可编辑对象的集合传递给算法