Saltar al contenido
ecdsa.com
Java · Kotlin

WebAuthn4J

WebAuthn4J, la biblioteca de verificación de WebAuthn en la que se basan la compatibilidad con llaves de acceso de Spring Security y Keycloak: sin valores predeterminados ocultos para la verificación del usuario, firmas solo en DER y rechazo tajante de los retrocesos del contador.
Licencia
Apache-2.0
Versión comprobada
0.31.11.RELEASE ·
Código fuente
github.com/webauthn4j/webauthn4j
Documentación
webauthn4j.github.io/webauthn4j/en/

Compatibilidad de un vistazo

WebAuthn

WebAuthn4J verifica las respuestas de registro y de autenticación —declaraciones de atestación de todos los formatos, claves COSE, indicadores, contadores y firmas— y deja a la relying party la construcción de las opciones. Spring Security y Keycloak la usan por debajo. La 0.31.11 depende de Jackson 3 (tools.jackson.*), lo que importa cuando tu aplicación todavía usa el com.fasterxml.jackson de Jackson 2 para su propio JSON, y su API lleva anotaciones @NotNull de JetBrains: compilar contra ella con el lanzador de archivo único java falló hasta que org.jetbrains:annotations estuvo en el classpath.

No hay ninguna política predeterminada de verificación del usuario que se pueda olvidar: RegistrationParameters y AuthenticationParameters reciben userVerificationRequired y userPresenceRequired como argumentos obligatorios. En la comprobación, un registro y una aserción con el indicador UV desactivado se verificaron cuando se pasó false y fallaron con UserNotVerifiedException cuando se pasó true. Mantén el valor coherente con el userVerification que envías en las opciones, como muestra el ejemplo.

Las firmas deben ser DER ASN.1: una firma de aserción r‖s en bruto de 64 bytes falló con BadSignatureException: Assertion signature is not valid., y una firma DER high-S se verificó. Un contador menor que el almacenado hace fallar el inicio de sesión con MaliciousCounterValueException: Malicious counter value is detected. Cloned authenticators exist in parallel., mientras que un contador que siempre vale 0, como el que notifican las llaves de acceso sincronizadas, pasa la comprobación. Las credenciales Ed25519 se verificaron de principio a fin.

createNonStrictWebAuthnManager() acepta cualquier declaración de atestación sin contrastarla con anclas de confianza, lo cual es correcto cuando pides la atestación none e incorrecto cuando la atestación es una decisión de política; en ese caso, configura las anclas de confianza y usa un gestor estricto.

El inspector de WebAuthn decodifica lo que envían los navegadores, y la búsqueda de AAGUID identifica los modelos de autenticador para las políticas de atestación.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/webauthn4j/run.sh ejecutó WebAuthn4J 0.31.11.RELEASE en la imagen de contenedor maven:3.9.16-eclipse-temurin-25 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Argumento de verificación del usuario

    mandatory

    userVerificationRequired y userPresenceRequired son argumentos obligatorios del constructor; con false, las respuestas sin el indicador UV se verificaron, y con true fallaron con UserNotVerifiedException.

    Registrado por la comprobación
    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
  • Codificación de la firma de la aserción

    der

    Una aserción ES256 correcta con firma DER se verificó.

    Registrado por la comprobación
    fact webauthn-assertion-encoding = der
  • Firma de aserción r‖s en bruto

    rejected

    Una firma r‖s en bruto de 64 bytes falló con BadSignatureException: Assertion signature is not valid.

    Registrado por la comprobación
    fact webauthn-raw-signature = rejected
  • Firma de aserción high-S

    accepted

    Una firma DER con s sustituido por n − s se verificó.

    Registrado por la comprobación
    fact webauthn-high-s = accepted
  • Contador de firmas que retrocede

    rejected

    Un contador de 3 tras un 5 almacenado falló con MaliciousCounterValueException; un 0 tras un 0 almacenado se verificó.

    Registrado por la comprobación
    fact webauthn-counter-regression = rejected
  • Credenciales Ed25519

    supported

    Una credencial Ed25519 (COSE -8) se registró y se autenticó correctamente.

    Registrado por la comprobación
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Uso

Crea un gestor de verificación y unos parámetros de registro que indiquen explícitamente los requisitos de verificación del usuario y de presencia del usuario.
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());
    }
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor maven:3.9.16-eclipse-temurin-25, y la ejecución falla si la salida no coincide.

Configuración
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Comando
java -cp 'lib/*' Usage.java
La salida debe coincidir (expresión regular)
uv required: true, up required: true, manager: WebAuthnManager

Bibliotecas