x509 web Worker环境中JWK的公钥



我想验证"firebase JWT代币">上的"strong>";Cloudflare工人"环境。

问题是firebaseauth没有提供标准的/.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上尝试了几个包,我发现所有的库要么不起作用,要么使用nodejs API(例如缓冲区(,这在CF环境上不起作用

有人能给我指一下如何吗

  • 将firebase公钥转换为JWK或
  • 将公钥转换为importKey可以接受的其他标准("raw" | "pkcs8" | "spki"

NB:我们在";CF Workers";"环境如此之全";nodejs";api不起作用

感谢

这里的关键(可以说(是PEM格式的私钥基于PKCS#8二进制格式"PEM";格式意味着底层二进制数据已被base64编码,并添加了类似--- BEGIN PRIVATE KEY ---的注释。WebCrypto可以理解PKCS#8二进制格式,但不能处理PEM。幸运的是,手动解码PEM并不太难。

以下是一些来自真实生产Cloudflare Worker的代码。

let pem = "[your PEM string here]";
// Parse PEM base64 format into binary bytes.
// The first line removes comments and newlines to form one continuous
// base64 string, the second line decodes that to a Uint8Array.
let b64 = pem.split('n').filter(line => !line.startsWith("--")).join("");
let bytes = new Uint8Array([...atob(b64)].map(c => c.charCodeAt(0)));
// Import key using WebCrypto API.
let key = await crypto.subtle.importKey("pkcs8", bytes,
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
false, ["verify"]);

请注意,PEM用于包装许多不同的格式。PKCS#8对于私钥是常见的。SPKI对于公钥是常见的(WebCrypto也支持这一点(。证书是另一种格式,我认为WebCrypto无法直接读取。

我不确定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函数的替代品。但它所做的只是创建一个certDer的sha1散列,并用base64对其进行编码,所以这可能并不困难。

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

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

最新更新