手动实现ES256算法JWT时jwt.io校验签名无效的问题排查
ES256格式JWT签名校验失败的根本原因
待签名输入不匹配JWT规范
- JWT规范中ES256算法的签名对象,是header与payload通过点号拼接后的原始ASCII字节流,整个SHA256哈希计算流程由签名库内部自动完成,不需要用户手动提前计算哈希值。
- 现有代码的操作逻辑存在本质偏差:手动对拼接串计算SHA256哈希、转换为全大写的十六进制字符串,再将这串文本字符本身作为待签名内容。这和jwt.io校验时使用的签名输入完全不同,即使曲线选择正确,签名结果也无法通过校验。
- 正确的待签名输入示例:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0的原始字节内容。
签名输出格式不匹配JWT规范
- ES256要求的签名输出格式为原始r+s拼接格式:将ECDSA签名计算得到的r、s两个256位大端整数,各自补足为32字节长度后直接拼接为总长度64字节的二进制串,不添加ASN.1/DER格式的封装头、长度标识字段。
- 两个测试阶段使用的库,默认输出的都是ASN.1/DER编码格式的签名(可通过Base64编码结果判断:DER格式的ECDSA签名Base64编码后通常以
MEQC开头)。这种格式是通用ECDSA签名的行业标准格式,但和JWT规范要求的raw拼接格式不兼容,即使签名消息完全正确,格式错误也会导致校验失败。
正确的签名流程修正点
- 直接传入header.payload的原始拼接字符串作为签名输入,不需要提前计算SHA256哈希值
- 调用签名接口时,指定签名输出为raw(r+s直接拼接)模式,关闭默认的DER编码输出
- 对最终生成的64字节raw签名做Base64url编码时,直接移除结尾的
=填充字符即可
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

