JWT 签名算法 #

算法概述 #

JWT 支持多种签名算法,主要分为三大类:

text
┌─────────────────────────────────────────────────────────────┐
│                    JWT 签名算法分类                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  对称算法(HMAC)                                     │   │
│  │  - HS256, HS384, HS512                               │   │
│  │  - 使用相同密钥签名和验证                             │   │
│  │  - 速度快,适合单服务                                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  非对称算法(RSA)                                    │   │
│  │  - RS256, RS384, RS512, PS256, PS384, PS512         │   │
│  │  - 私钥签名,公钥验证                                 │   │
│  │  - 适合多服务,公钥可公开                             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  椭圆曲线算法(ECDSA)                                │   │
│  │  - ES256, ES384, ES512                               │   │
│  │  - 私钥签名,公钥验证                                 │   │
│  │  - 更短密钥,同等安全                                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

对称算法(HMAC) #

什么是 HMAC? #

HMAC(Hash-based Message Authentication Code)是一种基于哈希的消息认证码,使用相同的密钥进行签名和验证。

text
┌─────────────────────────────────────────────────────────────┐
│                    HMAC 工作原理                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  签名过程:                                                  │
│                                                             │
│  消息 + 密钥 ────> HMAC 函数 ────> 签名                      │
│                                                             │
│  验证过程:                                                  │
│                                                             │
│  消息 + 密钥 ────> HMAC 函数 ────> 签名'                     │
│                        │                                    │
│                        ▼                                    │
│              签名' === 签名 ? 通过 : 失败                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

HMAC 算法列表 #

算法 哈希函数 输出长度 签名长度
HS256 SHA-256 256 位 32 字节
HS384 SHA-384 384 位 48 字节
HS512 SHA-512 512 位 64 字节

HS256 示例 #

javascript
const jwt = require('jsonwebtoken');

const payload = {
  sub: 'user-123',
  name: 'John Doe'
};

const secret = 'your-256-bit-secret';

const token = jwt.sign(payload, secret, { algorithm: 'HS256' });

console.log(token);

验证 JWT #

javascript
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });

console.log(decoded);

密钥要求 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HMAC 密钥要求                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  HS256:                                                    │
│  - 最小长度:32 字节(256 位)                              │
│  - 推荐长度:32 字节或更长                                  │
│                                                             │
│  HS384:                                                    │
│  - 最小长度:48 字节(384 位)                              │
│  - 推荐长度:48 字节或更长                                  │
│                                                             │
│  HS512:                                                    │
│  - 最小长度:64 字节(512 位)                              │
│  - 推荐长度:64 字节或更长                                  │
│                                                             │
│  生成安全密钥:                                              │
│  const crypto = require('crypto');                          │
│  const secret = crypto.randomBytes(32).toString('hex');     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

HMAC 优缺点 #

text
┌─────────────────────────────────────────────────────────────┐
│                    HMAC 优缺点分析                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  优点:                                                     │
│  ✅ 计算速度快                                              │
│  ✅ 实现简单                                                │
│  ✅ 签名长度短                                              │
│  ✅ 适合单服务场景                                          │
│                                                             │
│  缺点:                                                     │
│  ❌ 密钥必须安全分发                                        │
│  ❌ 所有验证方都需要密钥                                    │
│  ❌ 密钥泄露影响所有令牌                                    │
│  ❌ 不适合多方验证场景                                      │
│                                                             │
│  适用场景:                                                  │
│  - 单一授权服务器                                           │
│  - 内部服务间通信                                           │
│  - 简单应用架构                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

非对称算法(RSA) #

什么是 RSA? #

RSA 是一种非对称加密算法,使用一对密钥:私钥用于签名,公钥用于验证。

text
┌─────────────────────────────────────────────────────────────┐
│                    RSA 工作原理                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  密钥对:                                                    │
│  ┌─────────────────┐      ┌─────────────────┐              │
│  │     私钥        │      │     公钥        │              │
│  │   (保密)        │      │   (公开)        │              │
│  └────────┬────────┘      └────────┬────────┘              │
│           │                        │                        │
│           │ 签名                   │ 验证                   │
│           ▼                        ▼                        │
│  ┌─────────────────┐      ┌─────────────────┐              │
│  │  授权服务器     │      │  资源服务器     │              │
│  │  (签发 Token)   │      │  (验证 Token)   │              │
│  └─────────────────┘      └─────────────────┘              │
│                                                             │
│  优势:                                                     │
│  - 公钥可以公开分发                                         │
│  - 私钥只在授权服务器                                       │
│  - 多个资源服务器可独立验证                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

RSA 算法列表 #

算法 描述 签名长度
RS256 RSASSA-PKCS1-v1_5 SHA-256 256 字节
RS384 RSASSA-PKCS1-v1_5 SHA-384 256 字节
RS512 RSASSA-PKCS1-v1_5 SHA-512 256 字节
PS256 RSASSA-PSS SHA-256 256 字节
PS384 RSASSA-PSS SHA-384 256 字节
PS512 RSASSA-PSS SHA-512 256 字节

RS256 vs PS256 #

text
┌─────────────────────────────────────────────────────────────┐
│                    RS256 vs PS256                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  RS256(RSASSA-PKCS1-v1_5):                               │
│  - 传统 RSA 签名方案                                        │
│  - 广泛支持                                                 │
│  - 存在潜在安全问题                                         │
│                                                             │
│  PS256(RSASSA-PSS):                                      │
│  - 更安全的签名方案                                         │
│  - 添加随机盐值                                             │
│  - 推荐使用                                                 │
│                                                             │
│  选择建议:                                                  │
│  - 新项目推荐使用 PS256                                     │
│  - 兼容性要求高用 RS256                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

生成 RSA 密钥对 #

bash
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

RS256 签名示例 #

javascript
const jwt = require('jsonwebtoken');
const fs = require('fs');

const privateKey = fs.readFileSync('private.pem');
const publicKey = fs.readFileSync('public.pem');

const payload = {
  sub: 'user-123',
  name: 'John Doe'
};

const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });

console.log(token);

RS256 验证示例 #

javascript
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });

console.log(decoded);

RSA 优缺点 #

text
┌─────────────────────────────────────────────────────────────┐
│                    RSA 优缺点分析                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  优点:                                                     │
│  ✅ 公钥可公开分发                                          │
│  ✅ 私钥安全在授权服务器                                    │
│  ✅ 多个资源服务器可独立验证                                │
│  ✅ 适合分布式系统                                          │
│                                                             │
│  缺点:                                                     │
│  ❌ 计算速度较慢                                            │
│  ❌ 签名长度较长(256字节)                                  │
│  ❌ 密钥尺寸大                                              │
│                                                             │
│  适用场景:                                                  │
│  - 多个资源服务器                                           │
│  - 公开的 API                                               │
│  - OpenID Connect                                           │
│  - 微服务架构                                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

椭圆曲线算法(ECDSA) #

什么是 ECDSA? #

ECDSA(Elliptic Curve Digital Signature Algorithm)是一种基于椭圆曲线的数字签名算法,提供与 RSA 相同的安全级别,但使用更短的密钥。

text
┌─────────────────────────────────────────────────────────────┐
│                    ECDSA 工作原理                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  与 RSA 类似:                                              │
│  - 私钥签名,公钥验证                                       │
│  - 公钥可以公开分发                                         │
│                                                             │
│  椭圆曲线优势:                                              │
│  - 更短的密钥长度                                           │
│  - 更快的计算速度                                           │
│  - 更小的签名尺寸                                           │
│                                                             │
│  安全性对比:                                                │
│  ┌───────────────┬───────────────┐                         │
│  │  RSA 密钥长度  │  ECDSA 密钥长度│                         │
│  ├───────────────┼───────────────┤                         │
│  │   3072 位     │    256 位     │                         │
│  │   7680 位     │    384 位     │
│  │   15360 位    │    512 位     │                         │
│  └───────────────┴───────────────┘                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

ECDSA 算法列表 #

算法 曲线 哈希函数 签名长度
ES256 P-256 SHA-256 64 字节
ES384 P-384 SHA-384 96 字节
ES512 P-521 SHA-512 132 字节

生成 ECDSA 密钥对 #

bash
openssl ecparam -genkey -name prime256v1 -noout -out private.pem
openssl ec -in private.pem -pubout -out public.pem

ES256 签名示例 #

javascript
const jwt = require('jsonwebtoken');
const fs = require('fs');

const privateKey = fs.readFileSync('private.pem');
const publicKey = fs.readFileSync('public.pem');

const payload = {
  sub: 'user-123',
  name: 'John Doe'
};

const token = jwt.sign(payload, privateKey, { algorithm: 'ES256' });

console.log(token);

ES256 验证示例 #

javascript
const decoded = jwt.verify(token, publicKey, { algorithms: ['ES256'] });

console.log(decoded);

ECDSA 优缺点 #

text
┌─────────────────────────────────────────────────────────────┐
│                    ECDSA 优缺点分析                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  优点:                                                     │
│  ✅ 密钥长度短                                              │
│  ✅ 签名长度短                                              │
│  ✅ 计算速度快                                              │
│  ✅ 适合移动端和 IoT                                        │
│                                                             │
│  缺点:                                                     │
│  ❌ 实现复杂度较高                                          │
│  ❌ 部分旧系统不支持                                        │
│  ❌ 随机数质量影响安全                                      │
│                                                             │
│  适用场景:                                                  │
│  - 需要高性能的场景                                         │
│  - 移动应用                                                 │
│  - IoT 设备                                                 │
│  - 带宽受限环境                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

算法对比 #

安全性对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    算法安全性对比                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  安全级别        │  HMAC        │  RSA         │  ECDSA     │
│  ───────────────┼──────────────┼──────────────┼────────────│
│  128 位安全      │  HS256       │  RSA-3072    │  ES256     │
│  192 位安全      │  HS384       │  RSA-7680    │  ES384     │
│  256 位安全      │  HS512       │  RSA-15360   │  ES512     │
│                                                             │
│  推荐:                                                     │
│  - 128 位安全对于大多数应用足够                              │
│  - 高安全需求使用 192 位或更高                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

性能对比 #

text
┌─────────────────────────────────────────────────────────────┐
│                    算法性能对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  签名速度(快 → 慢):                                       │
│  HMAC > ECDSA > RSA                                         │
│                                                             │
│  验证速度(快 → 慢):                                       │
│  HMAC > RSA > ECDSA                                         │
│                                                             │
│  签名长度(短 → 长):                                       │
│  HMAC (32-64字节) < ECDSA (64-132字节) < RSA (256字节)      │
│                                                             │
│  密钥长度(短 → 长):                                       │
│  ECDSA (32-66字节) < HMAC (32-64字节) < RSA (2048+字节)     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

场景选择 #

text
┌─────────────────────────────────────────────────────────────┐
│                    算法选择指南                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  单服务架构:                                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  推荐:HS256                                         │   │
│  │  原因:简单、快速、签名短                             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  多服务/微服务架构:                                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  推荐:RS256 或 ES256                                │   │
│  │  原因:公钥可公开,资源服务器独立验证                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  公开 API:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  推荐:RS256                                         │   │
│  │  原因:兼容性好,公钥可公开分发                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  移动端/IoT:                                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  推荐:ES256                                         │   │
│  │  原因:签名短、计算快、带宽占用小                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  高安全需求:                                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  推荐:PS256 或 ES384                                │   │
│  │  原因:更安全的签名方案                               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

none 算法 #

什么是 none 算法? #

text
┌─────────────────────────────────────────────────────────────┐
│                    none 算法警告                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ⚠️  危险:none 算法表示不签名                              │
│                                                             │
│  特点:                                                     │
│  - 没有 Signature 部分                                      │
│  - 任何人都可以修改 Payload                                 │
│  - 极度不安全                                               │
│                                                             │
│  示例:                                                     │
│  { "alg": "none", "typ": "JWT" }                           │
│  { "sub": "admin", "role": "superuser" }                   │
│  (无签名)                                                   │
│                                                             │
│  安全建议:                                                  │
│  ❌ 永远不要使用 none 算法                                  │
│  ✅ 验证时明确拒绝 none 算法                                │
│  ✅ 始终验证 alg 字段                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

防护措施 #

javascript
const decoded = jwt.verify(token, publicKey, {
  algorithms: ['RS256', 'ES256']
});

算法混淆攻击 #

攻击原理 #

text
┌─────────────────────────────────────────────────────────────┐
│                    算法混淆攻击                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  攻击场景:                                                  │
│  1. 服务端使用 RS256(RSA 公钥验证)                        │
│  2. 攻击者将 alg 改为 HS256                                 │
│  3. 攻击者用公钥作为 HMAC 密钥签名                          │
│  4. 服务端可能误用公钥验证 HMAC                             │
│                                                             │
│  攻击流程:                                                  │
│  原始 Header: { "alg": "RS256" }                           │
│  篡改 Header: { "alg": "HS256" }                           │
│  使用公钥作为 HMAC 密钥签名                                 │
│                                                             │
│  防护措施:                                                  │
│  ✅ 明确指定允许的算法列表                                  │
│  ✅ 不要根据 JWT 的 alg 字段选择算法                        │
│  ✅ 使用类型安全的 JWT 库                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

防护代码 #

javascript
const decoded = jwt.verify(token, publicKey, {
  algorithms: ['RS256']
});

密钥管理 #

密钥生成 #

javascript
const crypto = require('crypto');

const hmacSecret = crypto.randomBytes(32).toString('hex');

const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 2048,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem'
  }
});

const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
  namedCurve: 'P-256',
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem'
  }
});

密钥存储 #

text
┌─────────────────────────────────────────────────────────────┐
│                    密钥存储最佳实践                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 环境变量                                                │
│     JWT_SECRET=your-secret-key                              │
│     适合:开发环境、简单应用                                │
│                                                             │
│  2. 密钥管理服务(KMS)                                     │
│     AWS KMS, Google Cloud KMS, Azure Key Vault             │
│     适合:生产环境、企业应用                                │
│                                                             │
│  3. 密钥文件                                                │
│     存储在安全目录,设置权限                                │
│     适合:服务器部署                                        │
│                                                             │
│  4. 硬件安全模块(HSM)                                     │
│     物理设备存储密钥                                        │
│     适合:高安全需求                                        │
│                                                             │
│  安全建议:                                                  │
│  ❌ 不要将密钥提交到代码仓库                                │
│  ❌ 不要在日志中打印密钥                                    │
│  ✅ 定期轮换密钥                                            │
│  ✅ 不同环境使用不同密钥                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

下一步 #

现在你已经了解了 JWT 的签名算法,接下来学习 JWT 基本使用,开始在实际项目中使用 JWT!

最后更新:2026-03-28