You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 18:43:17