1

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

我已经使用了 thejosejsrsasign库,但收效甚微。

一些我自己的代码来获得基础知识

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<........>' 

验证与josefalse

  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 核心模块。

4

2 回答 2

1

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

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

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

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

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

于 2021-10-27T19:53:26.143 回答
0

// 注意以下工作

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

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

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

于 2021-10-28T03:43:19.077 回答