为什么AWS Cognito会为JWT使用多个公钥?

时间:2017-05-15 11:45:39

标签: amazon-web-services jwt aws-cognito

当我下载特定用户池的JWT集时,请访问:https://cognito-idp。{region} .amazonaws.com / {userPoolId} /。熟知/ jwks.json

JSON包含2个键。我为池创建的所有用户似乎只使用其中一个密钥。

每个用户池有多个密钥的原因是什么?

2 个答案:

答案 0 :(得分:1)

来自decoding-aws-cognito-jwt

“首先,从下面的网址获取JSON Web密钥集(JWKS)文件。将区域和userPoolId替换为您的Cognito用户池的配置。

https://cognito-idp。{区域} .amazonaws.com / {userPoolId} /。众所周知/ jwks.json

此JSON哈希是一组JWK,因此名称为JWKS。它可能包含或不包含超过1 JWK。如果有多个JWK,我们应该使用哪一个进行解码?

在JWT中,令牌的标题部分中有一个名为kid的键,这是JWT的第一个哈希。而在每个JWK中,还有一个叫做kid的钥匙。因此,我们应该使用具有匹配的kid值的JWK来解码JWT。“

答案 1 :(得分:0)

根据the documentation

  

Amazon Cognito为每个用户池生成两对RSA加密密钥。私钥之一用于对令牌进行签名。

大概是出于安全原因。从有限的反复试验来看,似乎一个用来加密 id令牌,另一个用来加密访问令牌。也许目的是支持密钥旋转(如@ Michael-sqlbot所建议)。


一旦您了解了为什么(或不了解),问题就变成了用这两个键做什么

再次参考文档,以验证JWT签名,您需要:

  1. 解码ID令牌
  2. 将本地密钥ID(孩子)与公共孩子进行比较
  3. 使用公共密钥通过您的JWT库验证签名。

必须执行步骤1和2才能确定使用哪个 RSA加密密钥来加密您的JWT。

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')