Node.js中RSA密钥对验证JWT出现Invalid Signature错误求助
Node.js 20.5.1中RS256 JWT验证报「invalid signature」问题排查
错误信息
[16:39:56.959] FATAL (26460): invalid signature err: { "type": "JsonWebTokenError", "message": "invalid signature", "stack": JsonWebTokenError: invalid signature at U:\Coding\MCShop-API\node_modules\jsonwebtoken\verify.js:171:19 at getSecret (U:\Coding\MCShop-API\node_modules\jsonwebtoken\verify.js:97:14) at module.exports (U:\Coding\MCShop-API\node_modules\jsonwebtoken\verify.js:101:10) at verifyJWTToken (U:\Coding\MCShop-API\src\crypto.ts:28:37) at U:\Coding\MCShop-API\src\app.ts:39:45 at Layer.handle [as handle_request] (U:\Coding\MCShop-API\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (U:\Coding\MCShop-API\node_modules\express\lib\router\index.js:328:13) at U:\Coding\MCShop-API\node_modules\express\lib\router\index.js:286:9 at Function.process_params (U:\Coding\MCShop-API\node_modules\express\lib\router\index.js:346:12) at next (U:\Coding\MCShop-API\node_modules\express\lib\router\index.js:280:10) "name": "JsonWebTokenError" }
相关代码(crypto.ts)
import crypto from 'crypto'; import { readFileSync } from 'fs'; import { JwtPayload, sign, verify } from 'jsonwebtoken'; import { logger } from './app'; export function generateRSAKeyPair() { const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 512, publicKeyEncoding: { type: 'pkcs1', format: 'pem' }, privateKeyEncoding: { type: 'pkcs1', format: 'pem' } }); return { privateKey, publicKey }; } export function generateJWTToken(admin: boolean, username: string) { const key = readFileSync('private.key', { encoding: 'utf-8', flag: 'r' }); return sign({ admin, username }, key, { algorithm: 'RS256' }); } export function verifyJWTToken(token: string) { try { const key = readFileSync('public.key', { encoding: 'utf-8', flag: 'r' }); const verifiedToken = verify(token, key, { algorithms: ['RS256'] }) as JwtPayload; if (!verifiedToken) return false; return verifiedToken } catch (error) { logger.fatal(error); return false; } }
已确认事项
- key变量可正常读取文件内容,无undefined情况
- readFileSync未使用缓存
- 传入函数的参数均有效
- 该JWT已通过JWT.io验证有效
排查方向与解决方案
1. 密钥格式兼容问题
当前使用PKCS1格式密钥,部分版本jsonwebtoken对该格式的兼容性较差,建议切换为PKCS8标准格式:
修改generateRSAKeyPair的编码配置:
publicKeyEncoding: { type: 'spki', format: 'pem' }, // PKCS8公钥格式 privateKeyEncoding: { type: 'pkcs8', format: 'pem' } // PKCS8私钥格式
重新生成密钥对后重新测试验证流程。
2. 密钥文件内容一致性校验
确保读取的公钥与JWT.io上验证用的公钥完全一致(无多余换行、空格或编码错误),可在验证函数中添加日志输出对比:
// 在verifyJWTToken中添加 const key = readFileSync('public.key', { encoding: 'utf-8', flag: 'r' }); console.log('Loaded public key:', key); // 输出后与JWT.io使用的公钥逐字符对比
3. 库版本兼容性修复
Node.js 20.x对加密API有更新,旧版jsonwebtoken可能存在兼容问题,建议升级到最新稳定版:
npm update jsonwebtoken
也可替换为对新Node.js版本更友好的jose库:
npm install jose
jose验证示例代码:
import { jwtVerify } from 'jose'; import { readFileSync } from 'fs'; export async function verifyJWTToken(token: string) { try { const publicKey = await import('jose/util/key').then(m => m.importSPKI(readFileSync('public.key', 'utf-8'), 'RS256')); const { payload } = await jwtVerify(token, publicKey); return payload; } catch (error) { logger.fatal(error); return false; } }
4. RSA密钥长度调整
当前使用512位短密钥,部分库会对短密钥做限制,建议将模数长度提升到2048位(安全合规且兼容性更好):
modulusLength: 2048,
内容的提问来源于stack exchange,提问作者Liam
相关产品推荐
相关产品推荐

