【问题标题】:Tried to verify JWT signature by myself in nodejs to understand internal working of JWT, but decrypted signature gives wrong value试图在nodejs中自己验证JWT签名以了解JWT的内部工作,但解密后的签名给出了错误的值
【发布时间】:2021-08-02 21:24:39
【问题描述】:

为了了解数字签名和 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

【问题讨论】:

    标签: javascript node.js cryptography jwt


    【解决方案1】:

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

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

    给出以下输出:

    3031300d0609608648016503040201050004208041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53
    

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

    3031300d060960864801650304020105000420
    

    8041fb8cba9e4f8cc1483790b05262841f27fdcb211bc039ddf8864374db5f53
    

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

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

    【讨论】:

    • 非常感谢。哈希的每个示例都为哈希提供了可行的字符串值,所以我认为哈希只是文本。我不知道哈希是二进制的。我花了大约 3 到 4 个小时,你节省了我的时间。谢谢 :D 和不同的复制品,我明天会检查,因为现在是韩国的午夜。
    • 我粘贴了错误的哈希值。你发的那个是对的
    猜你喜欢
    • 2016-11-30
    • 2015-11-23
    • 2020-05-11
    • 2019-11-14
    • 2015-09-20
    • 2016-08-28
    • 2019-09-06
    • 2016-10-23
    相关资源
    最近更新 更多