NodeJS 加密验证。验证不适用于 ES256 P-256 JWT 但相同的代码适用于 RSA

问题描述 投票:0回答:1

我正在尝试编写一个“纯”NodeJS 函数来验证 JSON Web Token 上的签名,因为我要部署到的环境没有可用的这个包。

该代码适用于非椭圆曲线密钥,但如果我使用 P-256 和 ES256 则无效 - 我只是从 NodeJS 加密 verify.verfify 函数中得到令人讨厌的模糊“false”。

这是函数体:

function verifyToken(token, publicKey) {
    const [headerEncoded, payloadEncoded, signatureEncoded] = token.split('.')

    const payload = JSON.parse(Buffer.from(payloadEncoded, 'base64').toString())
    const verify = crypto.createVerify('sha256')
    verify.update(headerEncoded + '.' + payloadEncoded)
    
    const isValidSignature = verify.verify(publicKey, signatureEncoded, 'base64')
    if (!isValidSignature) {
        throw new Error('Invalid signature')
    }

    return payload
}

我正在这个有用的站点生成 JWK 格式的私钥和公钥:https://mkjwk.org/

我使用 jsonwebtoken 创建 JWT,然后将我的验证函数与 jsonwebtoken 版本进行比较。我知道我在每种情况下都使用私钥 - 但这只是为了缩短示例。

const jwt = require('jsonwebtoken')
const crypto = require('crypto')

function verifyToken(token, publicKey) {
    const [headerEncoded, payloadEncoded, signatureEncoded] = token.split('.')

    const payload = JSON.parse(Buffer.from(payloadEncoded, 'base64').toString())
    const verify = crypto.createVerify('sha256')
    verify.update(headerEncoded + '.' + payloadEncoded)
    
    const isValidSignature = verify.verify(publicKey, signatureEncoded, 'base64')
    if (!isValidSignature) {
        throw new Error('Invalid signature')
    }

    return payload
}

// Example RSA style key
const privateKey1 = {
    "key": {
        "p": "<redacted>",
        "kty": "RSA",
        "q": "<redacted>",
        "d": "<redacted>",
        "e": "AQAB",
        "use": "sig",
        "kid": "<redacted>",
        "qi": "<redacted>",
        "dp": "<redacted>",
        "alg": "RS256",
        "dq": "<redacted>",
        "n": "<redacted>"
    },
    "format": "jwk"
}

// Example ES256 - generated from https://mkjwk.org/
const privateKey2 = {
    "key": {
        "kty": "EC",
        "d": "<redacted>",
        "use": "sig",
        "crv": "P-256",
        "kid": "<redacted>",
        "x": "<redacted>",
        "y": "<redacted>",
        "alg": "ES256"
    },
    "format": "jwk"
}

var token1 = jwt.sign({ 'hello': 'world' }, privateKey1, 
    { 'algorithm' : privateKey1.key.alg, expiresIn: 60 * 60, 'header': { 'typ': 'JWT', 'alg' : privateKey1.key.alg } })

console.log('json web token  1 = ', jwt.verify(token1, privateKey1)) // This works
console.log('pure node token 1 = ', verifyToken(token1, privateKey1)) // This works

var token2 = jwt.sign({ 'hello': 'world' }, privateKey2, 
    { 'algorithm' : privateKey2.key.alg, expiresIn: 60 * 60, 'header': { 'typ': 'JWT', 'alg' : privateKey2.key.alg } })

console.log('json web token  2 = ', jwt.verify(token2, privateKey2)) // This works
console.log('pure node token 2 = ', verifyToken(token2, privateKey2)) // This fails

我需要提供其他选项来验证.verify吗?

我已经在 https://jwt.io 检查了令牌,那里的签名很好。

node.js jwt elliptic-curve verify
1个回答
0
投票

问题是由不兼容的 ECDSA 签名格式引起的

ECDSA签名主要指定两种格式,IEEE P1363和ASN.1/DER,见这里
在 JWT 的上下文中,根据定义使用 P1363,请参见here(步骤 1 到 4 描述了 P1363),即生成的令牌中的(Base64url 编码)签名具有 P1363 格式。
另一方面,NodeJS 的加密模块默认使用 ASN.1/DER,参见herehere,但可以通过属性

dsaEncoding
切换到 P1363。为此,必须按如下方式扩展密钥:

const privateKey2 = {
    "key": {
        "kty": "EC",
        "d": "v5PriRLFXqqILYFZkX2LWEbUQ_y_NCZXD6il5S6KPus",
        "use": "sig",
        "crv": "P-256",
        "kid": "f8124e12-98c4-4299-b93b-9d9a65f477db",
        "x":"r9FyRzL88VKR0wIhiDr8JFOTd5K_hVDb7uw0VDxOM7U",
        "y":"aqK5Wmb-UgjDPQ5cB1QGccATrtmgPDRnPzR5JCMPs_M",
        "alg": "ES256"
    },
    "format": "jwk",
    "dsaEncoding": "ieee-p1363" // switch to P1363
}

通过此更改,ECDSA 签名也已成功验证。

© www.soinside.com 2019 - 2024. All rights reserved.