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

WebAuthn4J

WebAuthn4J — библиотека проверки WebAuthn, на которой построены поддержка ключей доступа в Spring Security и Keycloak: никаких скрытых значений по умолчанию для проверки пользователя, подписи только в DER, а уменьшение счётчика всегда приводит к ошибке.
Лицензия
Apache-2.0
Проверенная версия
0.31.11.RELEASE ·
Исходный код
github.com/webauthn4j/webauthn4j
Документация
webauthn4j.github.io/webauthn4j/en/

Совместимость вкратце

WebAuthn

WebAuthn4J проверяет ответы регистрации и аутентификации — заявления об аттестации всех форматов, ключи COSE, флаги, счётчики и подписи, — а формирование параметров (options) оставляет доверяющей стороне. Spring Security и Keycloak используют её под капотом. Версия 0.31.11 зависит от Jackson 3 (tools.jackson.*), что важно, если ваше приложение для собственного JSON по-прежнему использует com.fasterxml.jackson из Jackson 2, а её API размечен аннотациями JetBrains @NotNull: код, который её использует, не компилировался при запуске одного исходного файла командой java, пока в classpath не появился org.jetbrains:annotations.

Политики проверки пользователя по умолчанию, о которой можно забыть, здесь нет: RegistrationParameters и AuthenticationParameters принимают userVerificationRequired и userPresenceRequired как обязательные аргументы. В тесте регистрация и assertion со сброшенным флагом UV проходили проверку, когда передавалось false, и завершались ошибкой UserNotVerifiedException при true. Согласуйте это значение с userVerification, которое вы передаёте в параметрах (options), как показано в примере.

Подписи должны быть в ASN.1 DER: подпись assertion в формате raw r‖s длиной 64 байта отклонена с ошибкой BadSignatureException: Assertion signature is not valid., а подпись в DER с high-S прошла проверку. Если счётчик меньше сохранённого, вход завершается ошибкой MaliciousCounterValueException: Malicious counter value is detected. Cloned authenticators exist in parallel., а счётчик, который всегда был равен 0, как у синхронизируемых ключей доступа, проверку проходит. Учётные данные Ed25519 прошли сквозную проверку.

createNonStrictWebAuthnManager() принимает любое заявление об аттестации, не сверяя его с якорями доверия: это правильно, если вы запрашиваете аттестацию none, и неправильно, если аттестация входит в вашу политику; в этом случае настройте якоря доверия и используйте строгий менеджер.

Инспектор WebAuthn декодирует то, что отправляют браузеры, а поиск по AAGUID определяет модели аутентификаторов для политик аттестации.

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

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

  • Аргумент проверки пользователя

    mandatory

    userVerificationRequired и userPresenceRequired — обязательные аргументы конструктора; при false ответы без флага UV прошли проверку, при true они отклонены с ошибкой UserNotVerifiedException.

    Что записала проверка
    fact uv-requirement-argument = mandatory
    evidence uv-requirement-argument: registration with UV clear, userVerificationRequired false: verified; true: UserNotVerifiedException: Verifier is configured to check user verified, but UV flag in authenticatorData is not set.
    evidence uv-requirement-argument: RegistrationParameters and AuthenticationParameters take userVerificationRequired and userPresenceRequired as required booleans
  • Кодировка подписи assertion

    der

    Проверка корректного assertion ES256 с подписью в DER прошла успешно.

    Что записала проверка
    fact webauthn-assertion-encoding = der
  • Подпись assertion в формате raw r‖s

    rejected

    Подпись в формате raw r‖s длиной 64 байта отклонена с ошибкой BadSignatureException: Assertion signature is not valid.

    Что записала проверка
    fact webauthn-raw-signature = rejected
  • Подпись assertion с high-S

    accepted

    Подпись в DER, в которой s заменили на n − s, прошла проверку.

    Что записала проверка
    fact webauthn-high-s = accepted
  • Уменьшение счётчика подписей

    rejected

    Значение счётчика 3 при сохранённом 5 привело к ошибке MaliciousCounterValueException; 0 при сохранённом 0 прошло проверку.

    Что записала проверка
    fact webauthn-counter-regression = rejected
  • Учётные данные Ed25519

    supported

    Учётные данные Ed25519 (COSE -8) успешно прошли регистрацию и аутентификацию.

    Что записала проверка
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Использование

Создайте менеджер проверки и параметры регистрации, в которых требования к проверке пользователя и присутствию пользователя заданы явно.
import com.webauthn4j.WebAuthnManager;
import com.webauthn4j.data.RegistrationParameters;
import com.webauthn4j.data.PublicKeyCredentialParameters;
import com.webauthn4j.data.PublicKeyCredentialType;
import com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier;
import com.webauthn4j.data.client.Origin;
import com.webauthn4j.data.client.challenge.DefaultChallenge;
import com.webauthn4j.server.ServerProperty;
import java.util.List;

public class Usage {
    public static void main(String[] args) {
        // "Non-strict" accepts every attestation format without trust anchors; pair it
        // with attestation "none" in the options, or configure trust anchors instead.
        WebAuthnManager manager = WebAuthnManager.createNonStrictWebAuthnManager();
        ServerProperty server = new ServerProperty(new Origin("https://example.com"), "example.com", new DefaultChallenge());

        // There is no default: every verification states UV and UP requirements explicitly.
        RegistrationParameters registration = new RegistrationParameters(server,
            List.of(new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256)),
            true, true);
        // manager.verify(manager.parseRegistrationResponseJSON(json), registration) does the rest.
        System.out.println("uv required: " + registration.isUserVerificationRequired()
            + ", up required: " + registration.isUserPresenceRequired() + ", manager: " + manager.getClass().getSimpleName());
    }
}

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера maven:3.9.16-eclipse-temurin-25, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Команда
java -cp 'lib/*' Usage.java
Вывод должен совпадать (регулярное выражение)
uv required: true, up required: true, manager: WebAuthnManager

Библиотеки