Node.js:签名验证(PS256)在 Node.js 中成功,但在 jwt.io 调试器中失败

Posted

技术标签:

【中文标题】Node.js:签名验证(PS256)在 Node.js 中成功,但在 jwt.io 调试器中失败【英文标题】:Node.js: Signature verification (PS256) succeeds in Node.js but fails in jwt.io debugger 【发布时间】:2019-07-15 07:25:03 【问题描述】:

我编写了一个测试脚本,我用它来签名,然后使用 PS256 算法验证 JWT。

我的代码验证 JWT 成功,但在 jwt.io 调试器中验证失败。

我正在使用jws@3.2.1

只有在使用 PS256 算法时才会发生这种情况,而不是在使用例如 RS256 时。

我做错了什么吗?

我生成了我的密钥:

openssl genpkey -algorithm RSA -out private_key2.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key2.pem -out public_key2.pem

你可以在 repl.it 上试试我的代码:https://repl.it/@SamArtuso/Nodejs-Signature-verification-PS256-succeeds-in-Nodejs

代码:

const  join  = require('path');
const  readFileSync  = require('fs');
const jws = require('jws');

const ALG = 'PS256';

/**********
 * SIGNING
 **********/

const PRIVATE_KEY_PATH = join(__dirname, './keys/private_key2.pem');

const privateKey = readFileSync(PRIVATE_KEY_PATH).toString();

const payload = 
  foo: 'bar',
;

const token = jws.sign(
  header:  alg: ALG ,
  payload,
  privateKey,
)

console.log('Token:');
console.log(token);

/************
 * VERIFYING
 ************/

const PUBLIC_KEY_PATH = join(__dirname, './keys/public_key2.pem');

const publicKey = readFileSync(PUBLIC_KEY_PATH).toString();

const result = jws.verify(token, ALG, publicKey);
if (result) 
  console.log('Verification successful.');
 else 
  console.error('Verification failed.');

私钥:

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDPR8CD/9V9fddR
vMk590JtuU4hPT9iPD/mYeWdvUTbkN2iPpg6LDADBntz8I9CzThCkgHmDQhh49Qz
AONiHfyHhKT6HHIBS78wGWfhE9ueFtkv19xisvFJTDO6IllGFMiioj8AkvOJwaY+
5ZKSFG32V0gMaglNSNTOh5KK6DsxgdH2KfMzn6uFwJkvsz1qwPiINu/rqvOQPOIK
JCbNHBOg/SvhPWBSEFDC9AkLm3ajhGAcnSlWi0KgUFz7iHuUR6s4GLdkc40uAooY
ExEWsZyoByT18tXln2hPAvE1Ata2PSbtLHuwvMQt4vzZi80K4BXqOiqvNMo1A1aT
oSQnmOwtAgMBAAECggEAPpHTPEVS5aHCCItrVtMbu1FvkzsQ0g+L3nh4vqfujDTr
olkwzIagK5meVH4uUKTwMbAvYIlYmWwTlx3ShcC1hRb2UgWaKGf8G4HfyKKc7djJ
0NZhUW3gxhZ5mttZhX0qn2VIjVzOpSvOijf0iaIfG3h3aD/t9OViT8G+6610iNmz
Oygmi9kQaBAS2A2sLSSMhdyddIPEpZ8QOHwGCRvBXWO100BCV9ROQzYxW+U9VPaJ
I0tXDL1L1H7y1EbxccPfOsfUPMtF9LQWQZ6ksJuSRwkBAnfKpzexguph29sLDjCQ
X23rQN/NwiU+zRmn+cW7VqkZsbSqucP9t+d19UdWpQKBgQDn9sThxAZvx4OS0noD
AWJX5CliDNlagmLqr4c0QoYI+fPL7FcK9tQYUZ869jnebX7UPHTuDhMoIvWknZbB
QhoogpRoX+XFtxzPwjtBBGc5TYacYnOR4xPSjvDlIrY7FQiH8TgpAGzenSBiD8O0
xW4zSaHLUDHEMFEyPq8tdMmBewKBgQDkwjUnVMPlA9AOD+KQNXgf2kV9z0ZA5idt
9WcsXsxpO2W6S0ntkr29Yr3VuuuJTRH7Fm+bPDJ6C1bfZ746hn3iLMZbMxgGVEf/
cN0nNXjfG5G7EEIU9WFges1I5rec/5W38kIco6soP36Qj5HcUBXt6AHy3k1IbEL2
84Bp1SZ0dwKBgF19mhCcXzPCKAePCVoYvrhJ31wDbb8K+i84m7e2cCtCAr7X/KUQ
op9Clni/MMezPgDwdPhVd+cfX/3+/fnaWIynRIVk0UkE6nnaAOPNkIUJ+A0jqQzN
hvnAXtsbSHM7oPqZgFcWMsrubVTYobpEMIw/SxSUt9oo1zD3Dse1YFntAoGBAIAL
AIuKU7f9gVhNpehINXvGxfzcpxsueEWBBgX87fe9CnzTJYc4CsJV4aIfZTVOEVF0
xnWipTJQ5IhZ3k2N+CpAG6ryl3D7fe1J9E+9C3H+UXzeZc6rZp0FP6Cdm4riOKBk
loLHTcdSevpZkjA6F3w5z9Vsft+Z0YW+2FLkvwiTAoGAdjUYcGTaEhGNtHQszAeY
2VbeVHwgnajKhaZpKMNTVp/AFgeS5k1lSATlT5wQHD3OAhUn+NAEwUOUBBWOpg+i
jML8loplEcVmdHkfGFsrIvzFFIIXQOWy77s/McTCh2jLkLYO8kIOrGy08JBiZnQ/
Ki/HFS/2yaUbG6UdH8FFp2g=
-----END PRIVATE KEY-----

公钥:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz0fAg//VfX3XUbzJOfdC
bblOIT0/Yjw/5mHlnb1E25Ddoj6YOiwwAwZ7c/CPQs04QpIB5g0IYePUMwDjYh38
h4Sk+hxyAUu/MBln4RPbnhbZL9fcYrLxSUwzuiJZRhTIoqI/AJLzicGmPuWSkhRt
9ldIDGoJTUjUzoeSiug7MYHR9inzM5+rhcCZL7M9asD4iDbv66rzkDziCiQmzRwT
oP0r4T1gUhBQwvQJC5t2o4RgHJ0pVotCoFBc+4h7lEerOBi3ZHONLgKKGBMRFrGc
qAck9fLV5Z9oTwLxNQLWtj0m7Sx7sLzELeL82YvNCuAV6joqrzTKNQNWk6EkJ5js
LQIDAQAB
-----END PUBLIC KEY-----

编辑:说明如何重现我的问题。

    运行我的脚本:npm init -f && npm install jws && node test.js 脚本将输出令牌和Verification successful. 将令牌复制并粘贴到 jwt.io 的“编码”字段中 将公钥复制并粘贴到“公钥或证书”字段中。 底部会出现一条红色的“无效签名”消息。

以下是我的脚本将生成的令牌示例。请记住,PS256 具有 概率 而不是 确定性 签名,因此每次运行脚本时签名都会有所不同。

eyJhbGciOiJQUzI1NiJ9.eyJmb28iOiJiYXIifQ.Rn6i-94ovuKOVRc2jzLVS40MpMmBkIfkyvF56JK3tM8wvg-DW943NNbXf01bhRdyoUj0A73NSQtz0kB4WfXN1uAH1omzNr0ww-iTfC23AX0OcjbsE7CcDz_ZQWWOzwEGGFVfV9ez5yn1pKRYVdFaKqApk3irP-ej_WGrrJfgeZVs683lNk0WjKOYhj6vsryuz52c0OEJ0UmYy7hSSfL38jgL6bdE0awg2DgiaU6qszZEkMjSnugoMobeAUUyOiXHsR79NryuhCQko-I9P1vKd1dEA3zM1iut5sW5FwB8K9Fi49gaQy0zHk72txJvUWdxW5ns0Svft3qLke5XMuqm2g

【问题讨论】:

请不要将此问题标记为重复。它的独特之处在于它特定于 PS256 算法。 所以您阅读了可能重复的问题和答案,对吧?!但是您并没有告诉我们,您在 jwt.io 上究竟做了什么,因为在大多数情况下,人们只是忘记在 jwt.io 上上传密钥。如果你能准确地告诉我们你在那里做了什么会更容易。 是的,我确实检查了潜在的重复项,但没有一个符合我的问题。我复制了我的令牌并将其粘贴到 jwt.io 上。我还将我的公钥粘贴到 jwt.io 上的相应字段中。我将更新问题的文本以反映这一点。任何人都应该能够重现我所做的。 那么请在问题中也显示你的token,以便人们可以尝试。 好的,我会这样做的。 【参考方案1】:

我尝试将您的令牌、公钥和私钥复制并粘贴到 jwt.io 的调试器中。以下是我的发现:

    如果只粘贴令牌,则令牌解码正确,但无法验证签名(这是显而易见的)。 如果您随后粘贴 公钥(这应该足以验证签名),仍然无法验证签名。这很奇怪? 如果您随后也粘贴了私钥(验证签名时不需要该密钥),则表明签名有效。

因此,显然问题归结为为什么只有在给出私钥的情况下才能验证令牌的签名......

这导致我假设私钥和公钥不匹配。因此,我尝试从您提供的私钥中重新生成(或更好地提取)公钥,并将该公钥与您建议的公钥进行比较。所以,我将你的私钥粘贴到一个名为privateKey.pem 的文件中,然后运行以下命令:

$ openssl rsa -in privateKey.pem -pubout

这给了我以下公钥:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz0fAg//VfX3XUbzJOfdC
bblOIT0/Yjw/5mHlnb1E25Ddoj6YOiwwAwZ7c/CPQs04QpIB5g0IYePUMwDjYh38
h4Sk+hxyAUu/MBln4RPbnhbZL9fcYrLxSUwzuiJZRhTIoqI/AJLzicGmPuWSkhRt
9ldIDGoJTUjUzoeSiug7MYHR9inzM5+rhcCZL7M9asD4iDbv66rzkDziCiQmzRwT
oP0r4T1gUhBQwvQJC5t2o4RgHJ0pVotCoFBc+4h7lEerOBi3ZHONLgKKGBMRFrGc
qAck9fLV5Z9oTwLxNQLWtj0m7Sx7sLzELeL82YvNCuAV6joqrzTKNQNWk6EkJ5js
LQIDAQAB
-----END PUBLIC KEY-----

很遗憾,这也是您提供的同一个公钥,所以我们可以从列表中勾掉这个想法。

所以,我开始在网上搜索,发现 this blog post by Auth0,他们描述了对 PS* 算法的支持的介绍,但根据文档,一切都应该按预期工作。嗯,更奇怪了?

所以,我尝试的下一件事是使用 jwt.io 的调试器、使用令牌的解码值、算法 PS256 和您提供的私钥对事物进行加密。这让我想到了这个令牌:

eyJhbGciOiJQUzI1NiJ9.eyJmb28iOiJiYXIifQ.wzkD7q5lH_qJw_V6DJLk72TDqpzA05KN3XHfEFOeAQgCriodoOLrgQ-p3ifIzK525puiV1hnXQuJ-6TEZRqiO8dqlPWQJbG1mLdTTx7ZwdUKsDjY6CMsmsskU8eVrzfaSDVmGvbjaWKq1_KGvtk4vMT0Z9m_YhUjD_SDVbCaZReEbzxta4APM7dMQ5mRFzD03JTwe05_AqayrfXdTungeBEJcMB0tY_4FjWiKbBZOpyQLebzPJ69VgpQFGvf6fPuKT6_3LTEHPrRNFL05OYpgRvayh3pEAF297aSCQ_SFi9slqDdVKkor-q8UDO-zM7J952uCkr7NkXV0Loq62_gMg P>

如果您将 this 令牌粘贴到 jwt.io 的调试器中,并使用 your 公钥,则签名有效。所以,基本上PS256 似乎可以与 jwt.io 一起使用,但无论出于何种原因,都不是为了你的令牌……

这最终让我想到,也许 Node.js 和 jwt.io 确实使用不同的方式来运行 PS256 算法。当然,因为它是一个标准,所以两者都应该以相同的方式工作,但显然它们没有。我不知道两者谁做错了,或者两者以不同的方式工作是否很好,因为我以前从未与PS256打交道。

很抱歉我在这里没有更好的答案,但我想找出真正原因的唯一方法是调试 Node.js 的加密代码,并查看 jwt.io 的源代码(据我所知,这不是公开的)。

在我看来,你所做的是正确的,我宁愿猜测这是 Node.js 或 jwt.io 中的一个错误,或者两者都有?

PS:写完这个答案后,我找到this issue,我猜原作者是你(不是吗?),那里的回复基本上得出了相同的结论,这是一个问题网站。所以也许这实际上是你能得到的最佳答案。

【讨论】:

其实jwt.io开源的。它的存储库是here。 JWT验证码为in this file;它似乎只是 node-jose 的包装。 好收获。我会尝试在本地运行它,看看是否能找到任何问题。【参考方案2】:

确保您正确复制密钥。

https://jwt.io/ 要求您复制包括BEGINEND 标记行

-----开始公钥-----

-----结束公钥-----

【讨论】:

我按照你的建议做了,但这并没有解决我的问题。 @kYuZz 那可能是因为你的token的签名是错误的...查看我的截图并与你的token比较,你会发现签名不同。 PS256 算法输出一个概率签名,即每次您签署相同的有效负载时它都会创建一个不同的签名。这就是为什么我们的签名不同。我相信我的问题与 jws 包中的错误有关。请看:github.com/brianloveswords/node-jws/issues/… @Sohail 如果您粘贴仅公钥(不是私钥),则会显示签名无效。请记住,验证令牌只需要公钥。

以上是关于Node.js:签名验证(PS256)在 Node.js 中成功,但在 jwt.io 调试器中失败的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 中未使用 RS256 算法生成 jsonwebtoken 令牌

Node.js关于微信支付V3版相关处理方法

在 PHP 中加密字符串并在 Node.js 中解密

在 Node.js 中验证 Azure idToken

如何在 node.js 中更改 jsonwebtoken 的标头

Node.js没有主机名验证的TLS连接