首页 > 解决方案 > 网络工作者环境中 JWK 的 x509 公钥

问题描述

我想在“Cloudflare 工作者”环境中验证“firebase JWT 令牌” 。

问题是 firebase-auth不提供标准/.well-known/jwks.json,而是提供x806 public key certificate (pem)格式

我正在使用“Webcrypto API”来完成加密工作,这就是我要做的

// Get CryptoKey
const key = await crypto.subtle.importKey(
  "jwk", // it's possible to change this format if the pem can be changed to other standards
  jwk, //  ?? Here is the missing piece
  { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
  false,
  ["verify"]
);

// Verify
const success = await crypto.subtle.verify(
  "RSASSA-PKCS1-v1_5",
  key,
  signature, // JWT signature
  data // JWT payload
);

我在 Github 上尝试了几个包,我发现的所有库要么不起作用,要么使用在 CF 环境中不起作用的 nodejs API(例如缓冲区)

有人可以指点我如何

注意:我们在“CF Workers”环境中,所以所有“nodejs”api 都不起作用

谢谢

标签: javascriptfirebaseweb-standardswebcrypto-apicloudflare-workers

解决方案


我不确定您对 CF 工作人员有什么可用的,但这可能是从以下内容开始:

const forge = require('node-forge')
const NodeRSA = require('node-rsa')
const {createHash} = require('crypto')
const base64url = require('base64url')

const getCertificateDer = certPem => {
    return forge.util.encode64(
        forge.asn1
            .toDer(forge.pki.certificateToAsn1(forge.pki.certificateFromPem(certPem)))
            .getBytes(),
    )
}

const getModulusExponent = certPem => {
    const nodeRsa = new NodeRSA()
    nodeRsa.importKey(certPem)

    const {n: modulus, e: exponent} = nodeRsa.exportKey('components-public')

    return {
        modulus,
        exponent,
    }
}

const getCertThumbprint = certDer => {
    const derBinaryStr = Buffer.from(certDer).toString('binary')

    const shasum = createHash('sha1')
    shasum.update(derBinaryStr)

    return shasum.digest('base64')
}

const getCertThumbprintEncoded = certDer => base64url.encode(getCertThumbprint(certDer))

const certPem = "<your pem certificate>"
const {modulus, exponent} = getModulusExponent(certPem)
const certDer = getCertificateDer(certPem)
const thumbprintEncoded = getCertThumbprintEncoded(certDer)

const jwksInfo = {
    alg: 'RSA256',
    kty: 'RSA',
    use: 'sig',
    x5c: [certDer],
    e: String(exponent),
    n: modulus.toString('base64'),
    kid: thumbprintEncoded,
    x5t: thumbprintEncoded,
}

由于您不能使用 Buffer 并且可能无法使用节点的加密库,因此您必须找到该getCertThumbprint功能的替代品。但它所做的只是创建一个 sha1 哈希certDer并对其进行 base64 编码,因此这可能并不困难。

更新:这可能会替代getCertThumbprint. 我做了一些测试,它似乎返回了与上面相同的值,但我没有用它来验证 JWT。

const sha1 = require('sha1')

const getCertThumbprint = certDer => btoa(sha1(certDer))

推荐阅读