AWS Cognito解码JWKS.json

时间:2016-08-04 16:50:55

标签: json amazon-web-services go jwt amazon-cognito

我正在验证AWS Cognito上的访问令牌。我要做的一件事是将密钥从jwks.json(用户池)转换为rsa公钥结构(取决于使用的JWT API)。

所以' n'的值并且' e'必须是大int(长)和int。

这两个变量的正确值是什么,例如: raw_n := "rdTmzrh7t0i_YN0MDLejnS0jXIFoSzRfFEbqf-bwGuRLnhLI4T3zGAk9HGZeAG6B5gg1D40Jsz1upo4E70VS0raGfSBPYPO7ZAJ2VCUUeblr9X_aWK4f294v4Cf3n8jZyFcGK9qhgcqy3DlHqqDANtjamWVtEhTRTFc-qoz1ScvHmPupsXlj1FsAEFEbVhP4705ez5gW3uQOoidrm38sPFwCN7g7xhA9CyzF04Zsjky55OfMCyWlIt7nljLx7ZRG3dVRD3vdEBI99qtxf43qMCWSPUk7Whn11Wf_u0xDrWhtGR9k599rKBBRWuqcujYYnFuOT0BeQIL25cePPK8lxw" raw_e := "AQAB"

我想这是Base64 URL编码。 我正在使用Go,当解码它们时,我有这些值:

Value of N = 21944212446918148307583266513211511961176501179660110972882270757464120247554839864039423096862533136364974693915136668416630333929475539217929135693935014796040215031370110392351836608149288005498214604075814317642291632460369313969964278103135047317799644939426174469533574133378199425168189176269507635563270873057483981163804984573367938044175828995131343800369166239708527999544583873649523245709447765091038652758632917341001616695912404965678061555823269517387737693508193881028187912513285002546706797506255288735805912213209305939523528571103281920754204216834697552752238593850648051945624431639572960454087 Value of E = 65537

最终值是: TokenSignature= GYZQKv7o8_o9E4ktVKZngYD4BS5QluOMwE-MRcJB432CmNimQm6JbvT3H48ECThe4f3sZ1KyVbgDJbyUnlkaAwMEBjMnlV7AUaZb-ifveM7kHM30BS5LCV_SCiCk-PvmWjeIHu9bR3EwG8azJCceD5A7gDLmhAtPN94gRy-opXJPAnaCba00AwKBd_pN3UH7LYu4u4EQ29eIfn4k4RCLuR31jr7ad3dvvjhhy658dQSHzSuPZGcN1-CRVSlrd0nk0Ba2t8W33LtjxM6wzPThWgh0fpy2XEDosGU_9FiXdEjUKisE3VHxroygQ8ekVWKHssa2eujXCx8OthWzaGag0w1 Signing String= eyJhbGciOiJSUzI1NiIsImtpZCI6ImpBNlFvakp0RkI0TmNIR1BmcS85ZWgzSHI2YnVXWEI0VzkxRTd5bWNjSk09In0.eyJleHAiOjE0NzAyNzgzMTEsInRva2VuX3VzZSI6ImFjY2VzcyIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20vdXMtZWFzdC0xX0d1OFlhVGg3MiIsImNsaWVudF9pZCI6IjRwNzNuYjhra3NsbHJrbTlzMzdzYXZsNzEzIiwidXNlcm5hbWUiOiJtYXRlbyIsImtpZCI6IiIsImFsZyI6IiIsImp0aSI6IjhmNTBiZmU4LWVlNGUtNGFkZi04MDQxLWU5MGM4YWJkZDExZCIsImlhdCI6MTQ3MDI3NDcxMSwic3ViIjoiYjFjMDZhMTktYjE5Yy00NGMwLTgzZjctODY2NTZjYzRmMjMxIn0 rsa.PublicKey{N:21944212446918148307583266513211511961176501179660110972882270757464120247554839864039423096862533136364974693915136668416630333929475539217929135693935014796040215031370110392351836608149288005498214604075814317642291632460369313969964278103135047317799644939426174469533574133378199425168189176269507635563270873057483981163804984573367938044175828995131343800369166239708527999544583873649523245709447765091038652758632917341001616695912404965678061555823269517387737693508193881028187912513285002546706797506255288735805912213209305939523528571103281920754204216834697552752238593850648051945624431639572960454087, E:65537}

但是,根据这些revisiter值,我仍然得到crypto/rsa: verification error。如何确定问题是来自JWT库还是值?

非常感谢

3 个答案:

答案 0 :(得分:1)

您应该能够使用以下程序或https://play.golang.org/p/VZqD5m057b生成的rsa.PublicKey验证签名。它基于https://github.com/mendsley/gojwk的代码(请参阅游乐场链接或版权归属的https://github.com/mendsley/gojwk/blob/master/LICENSE) - 我建议克隆该存储库并将其用作示例和模型。

package main

import (
    "crypto/rsa"
    "encoding/base64"
    "encoding/binary"
    "fmt"
    "math/big"
)

func main() {
    rawN := "rdTmzrh7t0i_YN0MDLejnS0jXIFoSzRfFEbqf-bwGuRLnhLI4T3zGAk9HGZeAG6B5gg1D40Jsz1upo4E70VS0raGfSBPYPO7ZAJ2VCUUeblr9X_aWK4f294v4Cf3n8jZyFcGK9qhgcqy3DlHqqDANtjamWVtEhTRTFc-qoz1ScvHmPupsXlj1FsAEFEbVhP4705ez5gW3uQOoidrm38sPFwCN7g7xhA9CyzF04Zsjky55OfMCyWlIt7nljLx7ZRG3dVRD3vdEBI99qtxf43qMCWSPUk7Whn11Wf_u0xDrWhtGR9k599rKBBRWuqcujYYnFuOT0BeQIL25cePPK8lxw"
    rawE := "AQAB"
    decodedE, err := base64.RawURLEncoding.DecodeString(rawE)
    if err != nil {
        panic(err)
    }
    // make sure that the E field is at least 4 bytes, pad if necessary
    if len(decodedE) < 4 {
        ndata := make([]byte, 4)
        copy(ndata[4-len(decodedE):], decodedE)
        decodedE = ndata
    }
    pubKey := &rsa.PublicKey{
        N: &big.Int{},
        E: int(binary.BigEndian.Uint32(decodedE[:])),
    }
    decodedN, err := base64.RawURLEncoding.DecodeString(rawN)
    if err != nil {
        panic(err)
    }
    pubKey.N.SetBytes(decodedN)
    fmt.Println(decodedN)
    fmt.Println(decodedE)
    fmt.Printf("%#v\n", *pubKey)
}

输出:

[173 212 230 206 184 123 183 72 191 96 221 12 12 183 163 157 45 35 92 129 104 75 52 95 20 70 234 127 230 240 26 228 75 158 18 200 225 61 243 24 9 61 28 102 94 0 110 129 230 8 53 15 141 9 179 61 110 166 142 4 239 69 82 210 182 134 125 32 79 96 243 187 100 2 118 84 37 20 121 185 107 245 127 218 88 174 31 219 222 47 224 39 247 159 200 217 200 87 6 43 218 161 129 202 178 220 57 71 170 160 192 54 216 218 153 101 109 18 20 209 76 87 62 170 140 245 73 203 199 152 251 169 177 121 99 212 91 0 16 81 27 86 19 248 239 78 94 207 152 22 222 228 14 162 39 107 155 127 44 60 92 2 55 184 59 198 16 61 11 44​​ 197 211 134 108 142 76 185 228 231 204 11 37 165 34 222 231 150 50 241 237 148 70 221 213 81 15 123 221 16 18 61 246 171 113 127 141 234 48 37 146 61 73 59 90 25 245 213 103 255 187 76 67 173 104 109 25 31 100 231 223 107 40 16 81 90 234 156 186 54 24 156 91 142 79 64 94 64 130 246 229 199 143 60 175 37 199] [0 1 0 1] rsa.PublicKey {N:21944212446918148307583266513211511961176501179660110972882270757464120247554839864039423096862533136364974693915136668416630333929475539217929135693935014796040215031370110392351836608149288005498214604075814317642291632460369313969964278103135047317799644939426174469533574133378199425168189176269507635563270873057483981163804984573367938044175828995131343800369166239708527999544583873649523245709447765091038652758632917341001616695912404965678061555823269517387737693508193881028187912513285002546706797506255288735805912213209305939523528571103281920754204216834697552752238593850648051945624431639572960454087,E:65537}

答案 1 :(得分:1)

我在nodeJS应用中使用了npm module jwk-to-pem 。如文档中所示:

var jwkToPem = require('jwk-to-pem'),
jwt = require('jsonwebtoken');

var jwk = { kty: 'EC', crv: 'P-256', x: '...', y: '...' },
pem = jwkToPem(jwk);

jwt.verify(token, pem);

您可以将pem导出到文件中并在代码中使用它。

答案 2 :(得分:0)

将 JWK 解码为 *rsa.PublicKey 可以通过将 JSON 解组为以下数据结构并使用以下方法来完成。

指向 this data structure 的链接:

// JSONKey represents a raw key inside a JWKs.
type JSONKey struct {
    Curve       string `json:"crv"`
    Exponent    string `json:"e"`
    ID          string `json:"kid"`
    Modulus     string `json:"n"`
    X           string `json:"x"`
    Y           string `json:"y"`
    precomputed interface{}
}

指向 this method 的链接:

// RSA parses a JSONKey and turns it into an RSA public key.
func (j *JSONKey) RSA() (publicKey *rsa.PublicKey, err error) {

    // Check if the key has already been computed.
    if j.precomputed != nil {
        var ok bool
        if publicKey, ok = j.precomputed.(*rsa.PublicKey); ok {
            return publicKey, nil
        }
    }

    // Confirm everything needed is present.
    if j.Exponent == "" || j.Modulus == "" {
        return nil, fmt.Errorf("%w: rsa", ErrMissingAssets)
    }

    // Decode the exponent from Base64.
    //
    // According to RFC 7518, this is a Base64 URL unsigned integer.
    // https://tools.ietf.org/html/rfc7518#section-6.3
    var exponent []byte
    if exponent, err = base64.RawURLEncoding.DecodeString(j.Exponent); err != nil {
        return nil, err
    }

    // Decode the modulus from Base64.
    var modulus []byte
    if modulus, err = base64.RawURLEncoding.DecodeString(j.Modulus); err != nil {
        return nil, err
    }

    // Create the RSA public key.
    publicKey = &rsa.PublicKey{}

    // Turn the exponent into an integer.
    //
    // According to RFC 7517, these numbers are in big-endian format.
    // https://tools.ietf.org/html/rfc7517#appendix-A.1
    publicKey.E = int(big.NewInt(0).SetBytes(exponent).Uint64())

    // Turn the modulus into a *big.Int.
    publicKey.N = big.NewInt(0).SetBytes(modulus)

    // Keep the public key so it won't have to be computed every time.
    j.precomputed = publicKey

    return publicKey, nil
}

如果您想从 AWS Cognito 等 JWK 资源中对 JWT 进行身份验证,我从一个执行此操作的项目中获取了这些示例:github.com/MicahParks/keyfunc