拓冰建站拓冰建站
首页 / 资讯中心 / 正文

JWT 与 Ed25519:从三段式结构到 Node.js 密钥实践

目录背景与目标一、JWT 是什么三段式结构三种常见签名算法怎么选二、Ed25519 密钥是什么三、公钥和私钥的对应关系重点四、本地用 Node.js 生成密钥对4.1 推荐crypto.generateKeyPairSync4.2 备选Web Crypto API五、用 jose 签发和验证 JWT实测跑通5.1 先避一个坑jsonwebtoken 不支持 EdDSA5.2 jose 完整示例5.3 谁持有谁六、踩过的坑与注意事项总结参考链接一篇团队内部分享文档讲清楚三件事JWT 是什么、Ed25519 密钥是什么、怎么在本地用 Node.js 生成密钥对并签发/验证 JWT。文中所有代码均已在本环境Node.js v22、jsonwebtoken 9.0.3、jose 5.10.0实测跑通。背景与目标后端做登录鉴权、微服务之间校验身份几乎绕不开 JWT而 JWT 用什么算法签名直接决定密钥怎么生成、怎么管理。这篇文章围绕三个问题展开JWT 是什么三段式结构、签名原理以及 HS256 / RS256 / EdDSA 怎么选。Ed25519 密钥是什么它和 RSA 有什么区别为什么越来越流行。本地怎么用 Node.js 生成密钥生成公钥/私钥的具体代码、PEM 文件长什么样、公钥和私钥是什么对应关系。读完你可以自己生成一对 Ed25519 密钥签发一个 JWT再用公钥验证它——全程不到 10 行代码。一、JWT 是什么三段式结构JWTJSON Web TokenRFC 7519是一串用.分成三段的字符串Header.Payload.SignatureHeader头声明签名算法和类型例如{alg:EdDSA,typ:JWT}。Payload载荷放业务声明claims如用户 ID、角色、过期时间exp。Signature签名对「头 载荷」两段做签名防止内容被篡改。下面是一个用 jose 实测签发出来的真实 JWT演示用密钥已废弃eyJhbGciOiJFZERTQSJ9.eyJ1c2VySWQiOjEwMDEsInJvbGUiOiJhZG1pbiIsImlhdCI6MTc4OTkxNDQzNSwiZXhwIjoxNzg5OTE4MDM1fQ.OQBwM9PELHtYORGJ6hCsJo1TFM_GG5iahfGiXJVM8E2M03QWdkHhtu7GUl_e5g1vfF75fRp2tLKRXhCBlHhUDQ把第一、二段做 base64url 解码就是 Header 和 Payload{alg:EdDSA,typ:JWT}{userId:1001,role:admin,iat:1789914435,exp:1789918035}签名的计算方式是EdDSA 算法下signature sign( base64url(header) . base64url(payload), privateKey )三种常见签名算法怎么选算法类型密钥特点与适用HS256对称HMAC签和验用同一个密钥实现简单但密钥要双方共享适合单服务内部RS256非对称RSA私钥签、公钥验公钥可公开分发RSA 密钥长、运算慢EdDSAEd25519非对称EdDSA私钥签、公钥验密钥和签名都很短、速度快多服务/跨团队验证的首选一句话结论只要存在「签发方」和「验证方」分离网关、其他服务、第三方就用非对称算法新项目优先 Ed25519。二、Ed25519 密钥是什么Ed25519是 EdDSAEdwards-curve Digital Signature Algorithm爱德华曲线数字签名算法在 Edwards25519 曲线上的具体实现标准见RFC 8032。它由一对密钥组成私钥Private Key / Seed32 字节随机数。自己保管绝不外传用来给数据签名。公钥Public Key32 字节。可以公开给任何人用来验证签名。签名结果固定 64 字节。为什么值得选它快签名、验签性能明显优于 RSA 和 ECDSA适合高并发场景。小32 字节公钥 64 字节签名。对比 RSA-2048公钥 256 字节、签名 256 字节。确定性同一份消息 同一把私钥签出的签名永远相同ECDSA 因带随机数每次签名不同。工程友好实现不依赖随机数生成器的正确性防随机数漏洞、无专利争议、抗侧信道攻击。一个容易混淆的点Ed25519 是签名算法不是加密算法。它的密钥对解决的是「验证是谁签的、内容有没有被改」而不是「把数据加密成密文」。需要加密时用 X25519/AES 等算法。三、公钥和私钥的对应关系重点一句话概括私钥是根公钥是从私钥确定性推导出来的私钥负责签名公钥负责验签。展开讲是四条单向推导Ed25519 的公钥 私钥32 字节种子经 Edwards25519 曲线基点做标量乘法得到。给定私钥必然能算出唯一的对应公钥。不可逆从公钥无法反推私钥——这是椭圆曲线离散对数问题的困难性保证的也是整套机制的安全根基。谁用哪个签发方持有私钥做签名任何拿到公钥的人其他服务、网关、客户端都可以验签但只有持有私钥的人才能伪造签名。JWK 里看得最清楚把 Ed25519 私钥导出为 JWKJSON Web Key格式会同时看到私钥字段d和公钥字段x——因为公钥就藏在私钥里实测输出{crv:Ed25519,d:o2IzaWtdUrLO1_TDx7E17YAq4kmq9oT9332V8quGR6Y,x:VyfuOErNXXFKiQLhSprC3gFjNMZkKDiHmFrDxrNeLM4,kty:OKP}实测还验证了两点用crypto.createPublicKey(私钥对象)从私钥导出的公钥与原始公钥逐字节一致同一把私钥对同一消息签名后crypto.verify用公钥验签通过。对应关系可以用下面这张图理解详细版见随文配套的生态图页面sign 签名传递verify 验签是否签发方持有私钥JWT验证方持有公钥签名有效?信任 payloaduserId1001...拒绝请求四、本地用 Node.js 生成密钥对4.1 推荐crypto.generateKeyPairSyncNode.js 内置的node:crypto模块原生支持ed25519无需安装任何依赖constcryptorequire(node:crypto);const{publicKey,privateKey}crypto.generateKeyPairSync(ed25519,{publicKeyEncoding:{type:spki,format:pem},// 公钥用 SPKI 编码的 PEMprivateKeyEncoding:{type:pkcs8,format:pem},// 私钥用 PKCS8 编码的 PEM});console.log(publicKey);console.log(privateKey);本环境实测输出演示用密钥-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAVyfuOErNXXFKiQLhSprC3gFjNMZkKDiHmFrDxrNeLM4 -----END PUBLIC KEY----- -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIKNiM2lrXVKyztf0w8exNe2AKuJJqvaE/d99lfKrhkem -----END PRIVATE KEY-----把输出写入文件即可得到public_key.pem和private_key.pemconstfsrequire(node:fs);fs.writeFileSync(public_key.pem,publicKey);fs.writeFileSync(private_key.pem,privateKey);PEM 是什么PEM 是「DER 二进制 base64 文本 首尾标记行」的封装格式。公钥用 SPKISubject Public Key Info标准私钥用 PKCS8 标准——注意私钥默认是明文存放的若担心泄露可以加口令加密privateKeyEncoding里传cipher: aes-256-cbc和passphrase。4.2 备选Web Crypto APINode.js 22 也支持标准的 Web Crypto API 生成 Ed25519 密钥const{subtle}globalThis.crypto;constkeyPairawaitsubtle.generateKey({name:Ed25519},true,[sign,verify]);// keyPair.privateKey / keyPair.publicKey 是 CryptoKey 对象// 通过 subtle.exportKey(pkcs8 | spki | jwk, key) 导出两者生成的密钥对可以互相兼容都遵循同一标准选哪个取决于你的代码风格同步场景用generateKeyPairSync更顺手浏览器/跨端统一用 Web Crypto。五、用 jose 签发和验证 JWT实测跑通5.1 先避一个坑jsonwebtoken 不支持 EdDSA最流行的jsonwebtoken库目前不支持 Ed25519/EdDSA。本环境实测jsonwebtoken 9.0.3constjwtrequire(jsonwebtoken);jwt.sign({userId:1001},privateKey,{algorithm:EdDSA});// 报错: algorithm must be a valid string enum value它只支持 HS/RS/ES/PS 系列。所以要用 Ed25519 签 JWT推荐jose标准化、零依赖、Node 和浏览器通用。5.2 jose 完整示例安装npminstalljose签发持有私钥的一方constjoserequire(jose);const{publicKey,privateKey}awaitjose.generateKeyPair(EdDSA);// 生产环境一般从 PEM 文件导入// const privateKey await jose.importPKCS8(pemString, EdDSA);consttokenawaitnewjose.SignJWT({userId:1001,role:admin}).setProtectedHeader({alg:EdDSA})// 算法固定为 EdDSA.setIssuedAt().setExpirationTime(1h).sign(privateKey);console.log(token);验证持有公钥的一方比如网关或另一个微服务const{payload}awaitjose.jwtVerify(token,publicKey);console.log(payload);// { userId: 1001, role: admin, iat: ..., exp: ... }实测输出签发的 token 验签通过payload 完整还原。上面的真实 token 示例就是这段代码跑出来的。5.3 谁持有谁认证服务签发私钥 private_key.pem —— 只能放在服务端绝不进前端代码 网关 / 资源服务 / 第三方验证公钥 public_key.pem —— 可以随便分发公钥可以写进配置、注册到 JWKS 端点/jwks返回公钥 JSON验证方自动拉取无需保密。六、踩过的坑与注意事项jsonwebtoken 不支持 EdDSA9.0.3 实测报algorithm must be a valid string enum value。用jose或在 jsonwebtoken 的算法白名单里选 RS256/ES256。验证必须显式约束算法验签时若不限制算法攻击者可能把alg改成none或 HS256 做「算法混淆攻击」。jose.jwtVerify默认按密钥类型校验算法安全性较好自己实现时务必做算法白名单。私钥泄露 全线失守私钥能伪造任意用户的 token。不要提交到 Git 仓库用环境变量或密钥管理服务KMS/Vault管理。别把 Ed25519 当加密用需要加密数据时用 X25519 AESEd25519 只解决「签名与验签」。HS256 的对称密钥不能公开HS256 只有一个共享密钥一旦泄露签和验都废了。多服务场景优先非对称。PEM 私钥默认明文落盘前按需加passphrase加密生产环境至少保证文件权限 600。总结JWT 三段式头 载荷 签名签名保证内容不可篡改多服务鉴权优先非对称算法。Ed25519 基于 Edwards25519 曲线的 EdDSA 签名方案32 字节私钥 32 字节公钥 64 字节签名快、小、确定性强是当前签名算法里性价比很高的选择。密钥对应关系私钥是根公钥由私钥单向推导私钥签名、公钥验签公钥无法反推私钥。Node.js 生成crypto.generateKeyPairSync(ed25519)一条命令拿到 PEM 文件JWT 签发/验证用josejsonwebtoken 实测不支持 EdDSA。下一步建议在本地跑一遍第四节和第五节的代码生成自己的密钥对试着用公钥去验签、把exp改成过去的时间看验证如何失败——跑通一遍这套机制就真正是你的了。参考链接RFC 7519 - JSON Web Token (JWT)RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA)Node.js Crypto 文档Node.js Web Crypto API 文档joseGitHubjsonwebtokenGitHubJWT.io - JWT 在线调试
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门