Перейти к содержимому
ecdsa.com
Java · Kotlin

java-jwt (Auth0)

java-jwt от Auth0 — компактная библиотека 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, неподписанный токен и токен HS256, подделанный с помощью текста PEM ключа EC, не прошли проверку объектом для ES256 с ошибкой AlgorithmMismatchException.

Защита работает, только пока Algorithm выбирается по конфигурации. JWT.require(Algorithm.none()) принимает неподписанные токены, а с Algorithm.HMAC256(pemBytes) поддельный токен HS256 прошёл проверку: код, который выбирает Algorithm по заголовку alg токена или загружает ключи как сырые байты, воспроизводит ошибки подмены алгоритма, от которых 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-байтовый формат raw r‖s, а подпись DER отклоняется с общей ошибкой The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA; длину подписи покажет отладчик JWT. Допуска на расхождение часов по умолчанию нет, а exp необязателен: пример добавляет withClaimPresence("exp") и acceptLeeway(30) вместе с проверкой издателя.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/java-jwt/run.sh запустила java-jwt (Auth0) 4.6.2 в образе контейнера maven:3.9.16-eclipse-temurin-25; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы JWS

    HS256HS384HS512RS256RS384RS512ES256ES384ES512

    Подпись и проверка работают для HS, RS и ES на 256/384/512; в фабрике 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 байта в формате raw r‖s.

    Что записала проверка
    fact jws-ecdsa-encoding = raw
  • Подпись в DER внутри JWS

    rejected

    Подпись ES256 в кодировке DER не проходит проверку с ошибкой 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: токен RS256 при проверке через JWT.require(ECDSA256) отклонён с ошибкой 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

    Токен HS256, подписанный текстом PEM в качестве ключа, не прошёл проверку объектом для 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

    По умолчанию расхождение часов не допускается: токен, срок действия которого истёк 3 секунды назад, отклонён.

    Что записала проверка
    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

Библиотеки