google-cloud-platform - 在 JS 中手动验证 GCP JWT 签名
问题描述
我已经能够在jwt.io的 Web UI 上验证 GCP ID 令牌,但我很难在 JS 中的代码中复制它。
我已经使用了 thejose
和jsrsasign
库,但收效甚微。
一些我自己的代码来获得基础知识
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 核心模块。
解决方案
在crypto.subtle.verify(algo, key, signature, data)
参数中,
signature
提供给函数的应该是 JWT 中提供的原始签名字符串的 URL-base64 解码版本的 TypedArray ( Uint8Array ) 。它不应该是原始签名字符串的 TypedArray。- 提供给函数的
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提供了有关签名解码的部分答案。
推荐阅读
- java - 如何在 Java 中调用具有多线程的 API?
- typescript - 休息或添加日期
- java - java.lang.StringIndexOutOfBoundsException:字符串索引超出范围:9
- javascript - 评估多个字符串 javascript 的更快方法
- html - How do I put an image next to a text in CSS?
- python - Appliyng data augmentation to all but one class in python
- java - 将字符串转换为日期 Java
- javascript - Locking the body scroll, blocks target element scrolling
- arrays - Pointer function only returns the first element in Float Array in C
- ios - Flutter Image Picker with iOS Method Channel