javascript - 试图在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
解决方案
发布的代码 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()
。
但是,我无法使用您发布的代码复制您发布的哈希值。
推荐阅读
- docker - 捕获 Docker 容器的 seccomp 违规
- docker - Docker-Compose Down 是否重启服务器
- verilog - gtkwave 中奇怪的测试台行为
- python - 使用现有列中的特定单词向数据框添加额外列
- akka - 与演员之外的 akka 接待员互动
- flutter - 没有定义“statefulwidget.createstate”和 setstate 的具体实现
- tfs - devops feed 显示来自上游来源的包。我可以禁用它吗?
- python - 带有不等列的 Pandas DataFrame 将用 NA 填充
- c - 理解 Gtk 模板对象
- java - 带弹簧靴的 Hikari cp .. 活动连接始终为 1 .. 不增加但不降低到 0