跳到主要内容
ecdsa.com
Java · Kotlin

java-jwt (Auth0)

Auth0 的 java-jwt:一个精简的 JWT 库,每个验证器都绑定到一个 Algorithm 对象,JWT.decode() 什么都不检查,算法只有 HS、RS 和 ES——没有 PS256,也没有 EdDSA。
许可证
MIT
检查的版本
4.6.2 ·
源代码
github.com/auth0/java-jwt
文档
github.com/auth0/java-jwt#readme

兼容性概览

JOSE 与 JWT

JWS 算法
HS256HS384HS512RS256RS384RS512ES256ES384ES512

java-jwt 用 JWT.create()…sign(algorithm) 构建令牌,用 JWT.require(algorithm)…build().verify(token) 验证令牌。Algorithm 对象同时携带算法和密钥,因此验证器只会接受这一种算法:RS256 令牌、未签名的令牌,以及用 EC 密钥的 PEM 文本伪造的 HS256 令牌,用 ES256 验证器验证时都报错 AlgorithmMismatchException。

这层防护只在 Algorithm 来自配置时才成立。JWT.require(Algorithm.none()) 会接受未签名的令牌,Algorithm.HMAC256(pemBytes) 也让伪造的 HS256 令牌验证通过了——如果代码根据令牌头部的 alg 选择 Algorithm,或把密钥加载为原始字节,就会重新引入这个 API 原本能防止的算法混淆缺陷。JWT.decode() 只解析,不检查签名,也不检查任何声明;它照样返回了一个一小时前就已过期的令牌。

支持的算法为 HMAC256/384/512、RSA256/384/512 和 ECDSA256/384/512——没有 PS256、ES256K 和 EdDSA——因此对于已改用 RSA-PSS 或 Ed25519 的颁发者,其令牌需要用 Nimbus 或 JJWT 处理。ES256 在签名时会拒绝 P-384 密钥。

ES256 签名是 64 字节的原始 r‖s,DER 签名会报出笼统的 The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA;JWT 调试器会显示签名长度。默认没有容差,exp 也是可选的:示例在检查颁发者的同时,加上了 withClaimPresence("exp") 和 acceptLeeway(30)。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/java-jwt/run.sh 于2026年10月20日在容器镜像 maven:3.9.16-eclipse-temurin-25 中运行了 java-jwt (Auth0) 4.6.2;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • JWS 算法

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    256/384/512 的 HS、RS 和 ES 签名与验证均成功;Algorithm 工厂中没有 PS*、ES256K 和 EdDSA。

    检查记录
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
    evidence jws-algorithms: Algorithm factory methods: HMAC256/384/512, RSA256/384/512, ECDSA256/384/512, none; no PS*, ES256K or EdDSA
  • ES256 签名编码

    raw

    ES256 签名是 64 字节的原始 r‖s。

    检查记录
    fact jws-ecdsa-encoding = raw
  • JWS 中的 DER 签名

    rejected

    DER 编码的 ES256 签名会报错 SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA。

    检查记录
    fact jws-der-signature = rejected
    evidence jws-der-signature: SignatureVerificationException: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA
  • 算法允许列表

    required

    验证器绑定到一个 Algorithm:用 JWT.require(ECDSA256) 验证 RS256 令牌,报错 AlgorithmMismatchException。

    检查记录
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: JWT.require(ECDSA256) on an RS256 token: AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
  • 未签名的令牌(alg none)

    opt-in

    ES256 验证器会拒绝未签名的令牌;JWT.require(Algorithm.none()) 则会接受它们。

    检查记录
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: JWT.require(ECDSA256): AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
    evidence jwt-alg-none: JWT.require(Algorithm.none()): verified
  • 把公钥用作 HMAC 密钥

    rejected

    以 PEM 文本为密钥的 HS256 令牌用 ES256 验证器验证失败,但 Algorithm.HMAC256(pemBytes) 让它验证通过了。

    检查记录
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: JWT.require(ECDSA256): AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
    evidence jwt-hmac-public-key: JWT.require(Algorithm.HMAC256(PEM bytes)): verified
  • 按 alg 检查曲线

    enforced

    Algorithm.ECDSA256 搭配 P-384 密钥签名失败,抛出 SignatureGenerationException。

    检查记录
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: SignatureGenerationException: The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withECDSA
  • 默认时钟偏差

    0s

    默认没有容差:三秒前过期的令牌被拒绝。

    检查记录
    fact jwt-clock-skew = 0s
  • 是否要求过期时间声明

    no

    不含 exp 的令牌验证通过;withClaimPresence("exp") 可将其设为必需。

    检查记录
    fact jwt-exp-required = no

用法

用 ES256 签名令牌,并在锁定颁发者、要求 exp 且容差为 30 秒的条件下验证。
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.time.Instant;

public class Usage {
    public static void main(String[] args) throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
        generator.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair pair = generator.generateKeyPair();
        Algorithm es256 = Algorithm.ECDSA256((ECPublicKey) pair.getPublic(), (ECPrivateKey) pair.getPrivate());

        String token = JWT.create().withSubject("user-42").withIssuer("https://auth.example.com")
            .withExpiresAt(Instant.now().plusSeconds(600)).sign(es256);

        // JWT.require() verifies; JWT.decode() only parses. Require exp and allow some leeway.
        DecodedJWT verified = JWT.require(es256).withIssuer("https://auth.example.com")
            .withClaimPresence("exp").acceptLeeway(30).build().verify(token);
        System.out.println("sub: " + verified.getSubject());
    }
}

代码片段测试工具会在容器镜像 maven:3.9.16-eclipse-temurin-25 中原样运行每个示例,只要输出不匹配,运行就会失败。

环境准备
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
命令
java -cp 'lib/*' Usage.java
输出必须匹配(正则表达式)
sub: user-42

库