1

我想验证一个使用 RS256 作为签名算法的谷歌 JWT 的签名(来自谷歌的证书: https: //www.googleapis.com/oauth2/v3/certs),以及我能找到的唯一库对于 Deno 处理 HS256 ( https://deno.land/x/djwt )。

我真的不喜欢整个密码游戏,也许有人知道我如何验证签名,也许已经有例子了?我真的不知道我需要用 SHA-256 散列什么或如何使用 RSA,当我尝试查找如何实现这一点时,我看到了很多技术解释,但没有关于如何处理的真实示例。

我通常只在 Node 上使用 Googles Scriptpackage 参见:https ://developers.google.com/identity/sign-in/web/backend-auth

我有用 SHA-256 散列的函数,但没有关于 RSA 的功能?

4

3 回答 3

3

从 djwt 1.6 版开始支持RS256

但是,这里我使用了一个名为God Crypto的加密模块来验证 RS256 签名令牌。God Crypto 具有解析JSON Web Key (JWK) 的功能,这是我们需要验证 Google 提供的 JWT 的功能。

首先,我展示了一个简短的示例,其中包含 JWK 的硬编码值和我为本演示制作的令牌:

import { RSA, encode } from "https://deno.land/x/god_crypto@v1.4.8/mod.ts";

const jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlctNjduZWt0WVRjOEpWWVBlV0g1c1dlN1JZVm5uMFN5NzQxZjhUT0pfQWMifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.hiKxeC66LIyVKOXjiOk7iScFPy_5-ATw7hEfqGij8sBZmwXAeTPT5BRFYHitFKSXomGqmy_63LLvg4zbhcTTmNf8XIeDAuLsC32soO5woSByisswWHVf8BgxMkI_FPW_oEtEQ8Xv3FL_1rF9j9Oy3jIjgjqhFhXUtsSQWAeuGYH-OQljFwiuO5Bqexcw-H71OEWvQLQof_6KJ0viJyte8QEwEVridyO834-ppHzeaoW2sTvZ22ZNfxPCew0Ul2V_TxHTtO7ZuJCZ81EmeIV6dYJ2GrYh3UN1x1PHy4-tEn-PL4otlaO3PYOcXfCHxHa6xtPsquzPZJnB1Vq8zULLfQ"

// public key in JSON Web Key(JWK) format:
const pubJWK = {
    "kty": "RSA",
    "e": "AQAB",
    "use": "sig",
    "kid": "W-67nektYTc8JVYPeWH5sWe7RYVnn0Sy741f8TOJ_Ac",
    "alg": "RS256",
    "n": "kFpGoVmBmmKepvBQiwq3hU9lIAuGsAPda4AVk712d3Z_QoS-5veGp4yltnyEFYyX867GOKDpbH7OF2uIjDg4-FPZwbuhiMscbkZzh25SQmfRtCT5ocUloQiopBcNAE-sd1p-ayUJWjhPrFoBrBLZHYxVEjY4JrWevQDj7kSeX7eJpud_VuZ77TNoIzj7d_iUuJUUlqF1ZF540igHKoVJJ6ujQLHh4ob8_izUuxX2iDq4h0VN3-uer59GsWw6OHgkOt85TsjMwYbeN9iw_7cNfLEYpSiH-sVHBCyKYQw7f8bKaChLxDRhUUTIEUUjGT9Ub_A3gOXq9TIi8BmbzrzVKQ"
}

// parse the JWK to RSA Key
const publicKey = RSA.parseKey(pubJWK)
const rsa = new RSA(publicKey)

// split the token into it's parts for verifcation
const [headerb64, payloadb64, signatureb64] = jwt.split(".")

// verify the signature based on the given public key
console.log(await rsa.verify(
    encode.base64url(signatureb64),
    headerb64 + "." + payloadb64,
    { algorithm: "rsassa-pkcs1-v1_5", hash: "sha256" },
  ))

可以直接运行上面的代码,得到结果

真的

为成功验证。

第二个示例从 google certs 端点加载 JWKS(JSON Web 密钥集),尝试找到匹配的密钥,然后在找到匹配的密钥时验证令牌。

令牌标头包含一个密钥 ID(“kid”),它标识应该用于验证的密钥。

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "W-67nektYTc8JVYPeWH5sWe7RYVnn0Sy741f8TOJ_Ac"
}
import { RSA, encode } from "https://deno.land/x/god_crypto@v1.4.8/mod.ts";
import { decode } from "https://deno.land/x/djwt@v2.2/mod.ts"

// the JWT that we want to verify
const jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlctNjduZWt0WVRjOEpWWVBlV0g1c1dlN1JZVm5uMFN5NzQxZjhUT0pfQWMifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.hiKxeC66LIyVKOXjiOk7iScFPy_5-ATw7hEfqGij8sBZmwXAeTPT5BRFYHitFKSXomGqmy_63LLvg4zbhcTTmNf8XIeDAuLsC32soO5woSByisswWHVf8BgxMkI_FPW_oEtEQ8Xv3FL_1rF9j9Oy3jIjgjqhFhXUtsSQWAeuGYH-OQljFwiuO5Bqexcw-H71OEWvQLQof_6KJ0viJyte8QEwEVridyO834-ppHzeaoW2sTvZ22ZNfxPCew0Ul2V_TxHTtO7ZuJCZ81EmeIV6dYJ2GrYh3UN1x1PHy4-tEn-PL4otlaO3PYOcXfCHxHa6xtPsquzPZJnB1Vq8zULLfQ"

// get the JSON Web Key Set (JWKS) from google certs endpoint
const certs = fetch("https://www.googleapis.com/oauth2/v3/certs");
var jwks = await certs.then((response) => {
  return response.json()
})


// decode the JWT to get the key Id ('kid') from the header
// in Version 2.2 of djwt decode returns a 3 tuple instead of an object
const [ header, payload, signature  ] = decode(jwt)
var keyId = Object(header).kid

// find the matching JSON Web Key (JWK) 
var pubjwk = findJWKByKeyId(String(keyId))

// parse the JWK to RSA Key
if (pubjwk) {
    const publicKey = RSA.parseKey(pubjwk)
    const rsa = new RSA(publicKey)

    // split the token into it's parts for verifcation
    const [headerb64, payloadb64, signatureb64] = jwt.split(".")

    // verify the signature based on the given public key
    console.log(await rsa.verify(
        encode.base64url(signatureb64),
        headerb64 + "." + payloadb64,
        { algorithm: "rsassa-pkcs1-v1_5", hash: "sha256" },
    ))
}
else
{
    console.log("key with kid (" + keyId +") not found")
}

// function to find a certain JWK by its Key Id (kid)
function findJWKByKeyId(kid:string) {
    return jwks.keys.find(
        function(x:string){ return Object(x).kid == kid }
    )
  }

正如 Scott Brady解释的那样,在您看到的令牌标头"alg": "RS256"rsa.verify()algorithm: "rsassa-pkcs1-v1_5"使用了 in,这是 RS256` 的长格式。RS

由于给定的令牌(在jwt.io上创建的示例)未由 Google 签名,因此找不到匹配的密钥,因此无法对其进行验证。使用您自己的 Google 签名的 JWT 来测试上述代码。

部分验证码基于God Crypto Github页面中的示例

于 2020-12-06T13:54:20.690 回答
0

让我们试试这个代码,更多细节访问这个页面jwt authentication in Deno

import { Context } from "https://deno.land/x/oak/mod.ts";
import users from "./users.ts";
import { makeJwt, setExpiration, Jose, Payload } from "https://deno.land/x/djwt/create.ts"
import key from './key.ts'

const header: Jose = {
  alg: "HS256",
  typ: "JWT",
}

export const login = async (ctx: Context) => {
  const {value} = await ctx.request.body();
  for (const user of users) {
    if (value.username === user.username && value.password === user.password) {
      const payload: Payload = {
        iss: user.username,
        exp: setExpiration(new Date().getTime() + 60000),
      }

      // Create JWT and send it to user
      const jwt = makeJwt({key, header, payload});
      if (jwt) {
        ctx.response.status = 200;
        ctx.response.body = {
          id: user.id,
          username: user.username,
          jwt,
        }
      } else {
        ctx.response.status = 500;
        ctx.response.body = {
          message: 'Internal server error'
        }
      }
      return;
    }
  }

  ctx.response.status = 422;
  ctx.response.body = {
    message: 'Invalid username or password'
  };
};
于 2020-06-06T09:05:02.543 回答
0

随着 Deno v1.18 的发布,https ://deno.land/x/jose 是最完整的 JWT/JWE/JWS/JWK 模块。

使用远程 JWKSet 进行验证非常简单

import * as jose from 'https://deno.land/x/jose@v4.3.8/index.ts'

const JWKS = jose.createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs'))

const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, {
  issuer: 'urn:example:issuer',
  audience: 'urn:example:audience'
})
console.log(protectedHeader)
console.log(payload)
于 2022-01-20T17:56:40.760 回答