Pular para o conteúdo
ecdsa.com
Java · Kotlin

WebAuthn4J

WebAuthn4J, a biblioteca de verificação WebAuthn por trás do suporte a chaves de acesso do Spring Security e do Keycloak: sem padrões ocultos para a verificação do usuário, assinaturas apenas em DER e falha incondicional quando o contador retrocede.
Licença
Apache-2.0
Versão verificada
0.31.11.RELEASE ·
Código-fonte
github.com/webauthn4j/webauthn4j
Documentação
webauthn4j.github.io/webauthn4j/en/

Visão geral da compatibilidade

WebAuthn

O WebAuthn4J verifica as respostas de registro e de autenticação — declarações de atestado de todos os formatos, chaves COSE, flags, contadores e assinaturas — e deixa a montagem das opções a cargo da parte confiável. O Spring Security e o Keycloak o usam internamente. A versão 0.31.11 depende do Jackson 3 (tools.jackson.*), o que importa quando sua aplicação ainda usa o com.fasterxml.jackson do Jackson 2 para o próprio JSON, e a API da biblioteca traz anotações @NotNull da JetBrains: compilar com o launcher java de arquivo único falhou enquanto org.jetbrains:annotations não estava no classpath.

Não existe uma política padrão de verificação do usuário que você possa esquecer: RegistrationParameters e AuthenticationParameters recebem userVerificationRequired e userPresenceRequired como argumentos obrigatórios. No teste, um registro e uma asserção com a flag UV desligada foram verificados quando se passou false e falharam com UserNotVerifiedException quando se passou true. Mantenha o valor coerente com o userVerification que você envia nas opções, como mostra o exemplo.

As assinaturas precisam estar em ASN.1 DER: uma assinatura de asserção r‖s bruta de 64 bytes falhou com BadSignatureException: Assertion signature is not valid., e uma assinatura DER high-S foi verificada. Um contador menor que o armazenado faz o login falhar com MaliciousCounterValueException: Malicious counter value is detected. Cloned authenticators exist in parallel., enquanto um contador que sempre foi 0, como informam as chaves de acesso sincronizadas, passa. Credenciais Ed25519 foram verificadas de ponta a ponta.

createNonStrictWebAuthnManager() aceita qualquer declaração de atestado sem conferi-la com âncoras de confiança, o que é correto quando você pede o atestado none e errado quando o atestado é uma decisão de política; nesse caso, configure âncoras de confiança e use um gerenciador estrito.

O inspetor de WebAuthn decodifica o que os navegadores enviam, e a consulta de AAGUID identifica modelos de autenticador para políticas de atestado.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/webauthn4j/run.sh executou WebAuthn4J 0.31.11.RELEASE na imagem de contêiner maven:3.9.16-eclipse-temurin-25 em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Argumento de verificação do usuário

    mandatory

    userVerificationRequired e userPresenceRequired são argumentos obrigatórios do construtor; com false, respostas sem a flag UV foram verificadas, e com true falharam com UserNotVerifiedException.

    Registrado pela verificação
    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
  • Codificação da assinatura de asserção

    der

    Uma asserção ES256 correta com assinatura DER foi verificada.

    Registrado pela verificação
    fact webauthn-assertion-encoding = der
  • Assinatura de asserção r‖s bruta

    rejected

    Uma assinatura r‖s bruta de 64 bytes falhou com BadSignatureException: Assertion signature is not valid.

    Registrado pela verificação
    fact webauthn-raw-signature = rejected
  • Assinatura de asserção high-S

    accepted

    Uma assinatura DER com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact webauthn-high-s = accepted
  • Contador de assinaturas que retrocede

    rejected

    Um contador igual a 3 depois de um 5 armazenado falhou com MaliciousCounterValueException; 0 depois de um 0 armazenado foi verificado.

    Registrado pela verificação
    fact webauthn-counter-regression = rejected
  • Credenciais Ed25519

    supported

    Uma credencial Ed25519 (COSE -8) foi registrada e autenticada com sucesso.

    Registrado pela verificação
    fact webauthn-ed25519 = supported
    evidence webauthn-ed25519: verified

Uso

Crie um gerenciador de verificação e parâmetros de registro que declarem explicitamente os requisitos de verificação do usuário e de presença do usuário.
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());
    }
}

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner maven:3.9.16-eclipse-temurin-25, e a execução falha se a saída não corresponder.

Configuração
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Comando
java -cp 'lib/*' Usage.java
A saída deve corresponder a (expressão regular)
uv required: true, up required: true, manager: WebAuthnManager

Bibliotecas