自行实现的JWT签名无法通过工具验证,问题出在哪里?
问题排查
- 签名流程使用了错误的Node.js Crypto API
你调用的crypto.privateEncrypt是用于RSA加密场景的API,不符合JWT RS256签名的标准要求:RSASSA-PKCS1-v1_5签名规范要求对哈希值封装ASN.1 DigestInfo结构后再执行私钥运算,privateEncrypt不会自动添加该结构,直接加密原始哈希值会导致签名结构不符合标准。
- 签名流程使用了错误的Node.js Crypto API
- 输出编码不符合JWT规范
JWT要求签名部分使用URL安全的Base64编码(Base64URL),你当前使用的普通Base64编码存在+、/、=等JWT不允许的字符,会导致校验失败。
- 输出编码不符合JWT规范
- 输入参数校验缺失
需要确认传入的input参数已经是Base64URL编码的header + "." + Base64URL编码的payload的拼接结果,输入格式不符合JWT要求也会直接导致签名校验失败。
- 输入参数校验缺失
修复方案
推荐直接使用Node.js Crypto标准的签名APIcrypto.createSign,自动符合RS256的签名规范,示例实现如下:
const createJwtRs256Signature = (headerPayloadStr: string, privateKey: string) => { // 初始化RS256签名器 const sign = crypto.createSign('RSA-SHA256'); // 写入待签名的 header.payload 内容 sign.update(headerPayloadStr); sign.end(); // 生成签名后转Base64URL编码 const signature = sign.sign(privateKey) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); return signature; };
如果要坚持用底层API实现,需要手动给SHA256哈希值添加对应的DigestInfo前缀,SHA256对应的前缀固定为Buffer.from([0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]),把前缀和你生成的哈希值拼接后再传入privateEncrypt即可,不过该方案容错性低,更推荐使用标准的Sign API。
内容的提问来源于stack exchange,提问作者Ansaar Dollie
相关产品推荐
相关产品推荐

