【问题标题】:Verifying GCP JWT signature manually in JS在 JS 中手动验证 GCP JWT 签名
【发布时间】:2021-12-12 23:33:50
【问题描述】:

我已经能够在jwt.io 的 Web UI 上验证 GCP ID 令牌,但我很难在 JS 代码中复制它。

我同时使用了josejsrsasign 库,但收效甚微。

一些我自己的代码来了解基础

function decodeJWT(jwtString: string) {
  const jwt = jwtString.match(
    /(?<header>[^.]+)\.(?<payload>[^.]+)\.(?<signature>[^.]+)/
  ).groups;

  // For simplicity trust that the urlBase64toStr function works
  // The parsed JWT is identical to what I see on jwt.io
  jwt.header = JSON.parse(urlBase64toStr(jwt.header));
  jwt.payload = JSON.parse(urlBase64toStr(jwt.payload));

  return jwt;
}

const jwt = decodeJWT('<....JWT string here......>')

const encoder = new TextEncoder();
const byteArrays = {
    signature: encoder.encode(jwt.signature),
    body: encoder.encode(
      JSON.stringify(jwt.header) + "." + JSON.stringify(jwt.payload)
    )
};

// Google's public certs at https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com
const cert = '-----BEGIN CERTIFICATE-----\n<........>' 

使用jose 验证会得到false

  const joseKey = await jose.importX509(cert, "RS256");
  console.log(
      await crypto.subtle.verify(
        joseKey.algorithm.name,
        joseKey,
        byteArrays.signature,
        byteArrays.body
      )
  ) 

// Note the following works

console.log(jose.jwtVerify(jwtRaw, joseKey))

使用jsrsaassign 也会得到false

  var c = new jsrsasign.X509();
  c.readCertPEM(cert);

  var jsRsaAssignKey = await crypto.subtle.importKey(
    "jwk",
    jsrsasign.KEYUTIL.getJWKFromKey(c.getPublicKey()),
    { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
    true,
    ["verify"]
  ); // Gets RSAKey first, then transforms into a JWK, then imported to get CryptoKey
  console.log(
      await crypto.subtle.verify(
        jsRsaAssignKey.algorithm.name,
        jsRsaAssignKey,
        byteArrays.signature,
        byteArrays.body
      )
  )

我哪里错了?

注意:请不要推荐 NodeJS 库。我需要运行脚本的环境不支持 Node 核心模块。

【问题讨论】:

  • 我不是 JavaScript 开发人员。我不确定您要在这里做什么 encoder.encode(jwt.signature),。 JWT 签名采用 base-64 编码。因此,我认为您需要在调用 crypto.subtle.verify 之前对签名进行 base64 解码。

标签: google-cloud-platform jwt x509 webcrypto-api jose


【解决方案1】:

//注意以下工作

console.log(await jose.jwtVerify('<..jwt string here......>', joseKey))

那么你也可以使用它,jose 是一个在 Node.js 之外工作的通用模块。

不过,您的手动验证输入错误。 data 应该只是没有第二个点和签名的 jwt,编码为 base64url,表示为 ArrayBufferView。 signature 应该是从base64url解码的JWT签名部分,表示为ArrayBufferView。

【讨论】:

    【解决方案2】:

    crypto.subtle.verify(algo, key, signature, data)参数中,

    1. 提供给函数的signature 应该是 JWT 中提供的 URL-base64 解码 版本的原始签名字符串的 TypedArray (Uint8Array)。它应该是原始签名字符串的 TypedArray。
    2. 提供给函数的data 应该是原始JWT 字符串中&lt;header&gt;.&lt;payload&gt; 提供 字符串的TypedArray。它应该是看起来像{"typ": "JWT"}.{"iss": "https://issuer.com/"}的解码、解析和字符串化的标头和有效负载

    还需要注意的是,JS 内置的TextEncoder 默认不会返回正确的Uint8Array。不要使用它将字符串转换为 TypedArray,而是使用下面给出的 strToUint8Array 函数。

    function strToUint8Array(value: string): Uint8Array {
      return Uint8Array.from(
        Array.from(value).map((letter) => letter.charCodeAt(0))
      );
    }
    
    

    感谢@John Hanley 提供有关签名解码的部分答案。

    【讨论】:

      猜你喜欢
      • 2019-05-21
      • 2018-10-11
      • 2016-08-28
      • 1970-01-01
      • 2019-11-14
      • 2016-12-27
      • 2018-09-16
      • 1970-01-01
      相关资源
      最近更新 更多