首页 > 解决方案 > 试图在nodejs中自己验证JWT签名以了解JWT的内部工作,但解密后的签名给出了错误的值

问题描述

为了了解数字签名和 JWT 的工作原理,我尝试使用 RS256 算法验证 JSON Web Token。但是,当我解密 JWT 的签名部分时,它会给出非字符串值,因此我无法将该值与计算的哈希值进行比较。有人可以告诉我我在代码中误解了哪一部分吗?我使用了 RS256 算法 JWT 令牌,所有值都在https://jwt.io/中给出。如果向下滚动并选择 RS256 选项,您可以获得 base64url 编码的 JWT 和公钥/私钥。我想我正在解密 JWT 的错误部分,但找不到它。

const base64url = require('base64url')
const crypto = require('crypto')
const fs = require('fs')

function readKeyPair(path) {
    return {
        publicKey: fs.readFileSync(path.publicPath),
        privateKey: fs.readFileSync(path.privatePath)
    }
}

function encryptWithPrivateKey(privateKey, message) {
    const bufferMessage = Buffer.from(message, 'utf8');
    return crypto.privateEncrypt(privateKey, bufferMessage)
}

function decryptWithPublicKey(publicKey, buffer) {
    return crypto.publicDecrypt(publicKey, buffer);
}

function hashMessage(message, algorithm) {
    const hash = crypto.createHash(algorithm);
    hash.update(message);
    const hashValue = hash.digest('hex')
    return hashValue;
}

const JWT = (
    'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZS' +
    'I6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZ' +
    'S82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExR' +
    'EkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQG' +
    'xHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8Ocaar' +
    'A8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618i' +
    'Yv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA'
)

const KEY_PAIR_PATH = {
    publicPath: 'rsa_pub.pem',
    privatePath: 'rsa_priv.pem'
}

const jwtParts = JWT.split('.')
const header = base64url.decode(jwtParts[0])
const payload = base64url.decode(jwtParts[1])
const signature = base64url.toBuffer(jwtParts[2])

console.log(header)
console.log(payload)
console.log(signature)

const keyPair = readKeyPair(KEY_PAIR_PATH)
const decryptedHashValue = decryptWithPublicKey(keyPair.publicKey, signature);

const newHash = hashMessage(jwtParts[0] + '.' + jwtParts[1], 'SHA256')
console.log()
console.log(decryptedHashValue.toString())
console.log()
console.log(newHash)

这是代码的输出

{"alg":"RS256","typ":"JWT"}
{"sub":"1234567890","name":"John Doe","admin":true,"iat":1516239022}
<Buffer 3c eb 2d 19 eb 5f 03 2b 5a 65 2f 36 c0 77 23 a1 3c a8 aa 13 32 c5 78 96 75 1e cd 9f b0 36 f4 33 52 97 41 22 5c b7 70 27 ac 42 e8 07 e0 65 61 75 6c eb ... 206 more bytes>

010     `�He �A����O��H7��Rb�'��!9���Ct�_S

8041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53

标签: javascriptnode.jscryptographyjwt

解决方案


发布的代码 UTF8 解码 的值decryptedHashValue,破坏数据并产生乱码。必须使用二进制到文本编码(例如 Base64 或十六进制编码)将任意二进制数据(例如密文或哈希值)转换为字符串。由于hashMessage()十六进制编码数据,十六进制编码是合适的选择:

console.log(decryptedHashValue.toString('hex'))

给出以下输出:

3031300d0609608648016503040201050004208041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53

这个值可以分为以下两部分:

3031300d060960864801650304020105000420

8041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53

第二部分完全对应于用newHash确定的哈希值newHash()。第一部分对应于SHA-256 的摘要 ID,它在 PKCS1 v1.5 填充(RSASSA-PKCS1-v1_5,s. RFC8017)的情况下被添加,默认填充由privateEncrypt()和使用publicDecrypt()

但是,我无法使用您发布的代码复制您发布的哈希值。


推荐阅读