试图在 nodejs 中自己验证 JWT 签名以了解 JWT 的内部工作,但解密的签名给出了错误的值
Tried to verify JWT signature by myself in nodejs to understand internal working of JWT, but decrypted signature gives wrong value
为了了解数字签名和 JWT 的工作原理,我尝试使用 RS256 算法验证 JSON Web 令牌。但是,当我解密 JWT 的签名部分时,它会给出非字符串值,因此我无法将该值与计算出的哈希值进行比较。有人可以告诉我我在代码中误解了什么部分吗?我使用了一个RS256算法的JWT token,所有的值都在https://jwt.io/中给出。如果向下滚动并选择 select RS256 选项,您可以获得 base64url 编码的 JWT 和 public/private 密钥。我猜我解密的是 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
。第一部分对应于digest ID of SHA-256, which is prepended in the case of PKCS1 v1.5 padding (RSASSA-PKCS1-v1_5, s. RFC8017), the default padding used by privateEncrypt()
and publicDecrypt()
.
但是,我无法使用您发布的代码重现您发布的散列值。
为了了解数字签名和 JWT 的工作原理,我尝试使用 RS256 算法验证 JSON Web 令牌。但是,当我解密 JWT 的签名部分时,它会给出非字符串值,因此我无法将该值与计算出的哈希值进行比较。有人可以告诉我我在代码中误解了什么部分吗?我使用了一个RS256算法的JWT token,所有的值都在https://jwt.io/中给出。如果向下滚动并选择 select RS256 选项,您可以获得 base64url 编码的 JWT 和 public/private 密钥。我猜我解密的是 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
。第一部分对应于digest ID of SHA-256, which is prepended in the case of PKCS1 v1.5 padding (RSASSA-PKCS1-v1_5, s. RFC8017), the default padding used by privateEncrypt()
and publicDecrypt()
.
但是,我无法使用您发布的代码重现您发布的散列值。