为啥 AWS Cognito 对 JWT 使用多个公有密钥?
Posted
技术标签:
【中文标题】为啥 AWS Cognito 对 JWT 使用多个公有密钥?【英文标题】:Why does AWS Cognito use multiple public keys for JWTs?为什么 AWS Cognito 对 JWT 使用多个公有密钥? 【发布时间】:2017-10-14 04:34:00 【问题描述】:当我下载特定用户池的 JWT 集时,可在以下位置获得:https://cognito-idp.region.amazonaws.com/userPoolId/.well-known/jwks.json
JSON 包含 2 个键。我为池创建的所有用户似乎只使用这些密钥之一。
每个用户池有多个密钥的原因是什么?
【问题讨论】:
用于处理密钥轮换期间的重叠? @Michael-sqlbot 你能详细说明一下吗?在阅读您的评论之前,我一直在使用数组中两个键中的第一个,并且我对键轮换一无所知。我刚刚看到了底部的代码示例:aws.amazon.com/blogs/mobile/…,它查找所需的密钥。我在一个似乎工作了一段时间的 api 中只使用了第一个,所以现在我担心它可能会在某个时候失败。 【参考方案1】:根据the documentation:
Amazon Cognito 为每个用户池生成两对 RSA 加密密钥。其中一个私钥用于对令牌进行签名。
大概是出于安全原因。从有限的反复试验看来,一个用于加密 id 令牌,另一个用于加密 访问令牌。或者也许目的是支持密钥轮换(正如@Michael-sqlbot 所建议的那样)。
一旦你理解了为什么(或不明白),问题就变成了如何用这两个键来做。
再次参考文档,验证您需要的 JWT 签名:
-
解码 ID 令牌
将本地密钥 ID(孩子)与公共孩子进行比较
使用公钥来验证使用您的 JWT 库的签名。
步骤 1 和 2 是确定用于加密 JWT 的 RSA 加密密钥所必需的。
import jsonwebtoken from 'jsonwebtoken'
import jwkToPem from 'jwk-to-pem'
const jsonWebKeys = [ // from https://cognito-idp.us-west-2.amazonaws.com/<UserPoolId>/.well-known/jwks.json
"alg": "RS256",
"e": "AQAB",
"kid": "ABCDEFGHIJKLMNOPabc/1A2B3CZ5x6y7MA56Cy+6ubf=",
"kty": "RSA",
"n": "...",
"use": "sig"
,
"alg": "RS256",
"e": "AQAB",
"kid": "XYZAAAAAAAAAAAAAAA/1A2B3CZ5x6y7MA56Cy+6abc=",
"kty": "RSA",
"n": "...",
"use": "sig"
]
function validateToken(token)
const header = decodeTokenHeader(token) // "kid":"XYZAAAAAAAAAAAAAAA/1A2B3CZ5x6y7MA56Cy+6abc=", "alg": "RS256"
const jsonWebKey = getJsonWebKeyWithKID(header.kid)
verifyJsonWebTokenSignature(token, jsonWebKey, function(err, decodedToken)
if (err)
console.error(err)
else
console.log(decodedToken)
)
function decodeTokenHeader(token)
const [headerEncoded] = token.split('.')[0]
const buff = new Buffer(headerEncoded, 'base64')
const text = buff.toString('ascii')
return JSON.parse(text)
func getJsonWebKeyWithKID(kid)
for (let jwk of jsonWebKeys)
if (jwk.kid == kid)
return jwk
return null
function verifyJsonWebTokenSignature(token, jsonWebKey, clbk)
const pem = jwkToPem(jsonWebKey)
jsonwebtoken.verify(token, pem, algorithms: ['RS256'] , function(err, decodedToken)
return clbk(err, decodedToken)
)
validateToken('xxxxxxxxx.XXXXXXXX.xxxxxxxx')
【讨论】:
【参考方案2】:来自decoding-aws-cognito-jwt
“首先,从下面的 url 获取 JSON Web Key Set (JWKS) 文件。将 region 和 userPoolId 替换为您的 Cognito 用户池的配置。
https://cognito-idp.region.amazonaws.com/userPoolId/.well-known/jwks.json
这个 JSON 散列是一组 JWK,因此得名 JWKS。它可能包含也可能不包含超过 1 个 JWK。如果有多个 JWK,我们应该使用哪一个来进行解码?
在JWT中,在token的header部分有一个key叫做kid,也就是JWT的第一个hash。而在每个 JWK 中,还有一个名为 Kid 的密钥。因此,我们应该使用具有匹配 Kid 值的 JWK 来解码 JWT。”
【讨论】:
以上是关于为啥 AWS Cognito 对 JWT 使用多个公有密钥?的主要内容,如果未能解决你的问题,请参考以下文章
更改 AWS Cognito 访问令牌 JWT 中的加密算法