Search in sources :

Example 1 with X509ClientCertificateLookup

use of org.keycloak.services.x509.X509ClientCertificateLookup in project keycloak by keycloak.

the class X509ClientAuthenticator method authenticateClient.

@Override
public void authenticateClient(ClientAuthenticationFlowContext context) {
    X509ClientCertificateLookup provider = context.getSession().getProvider(X509ClientCertificateLookup.class);
    if (provider == null) {
        logger.errorv("\"{0}\" Spi is not available, did you forget to update the configuration?", X509ClientCertificateLookup.class);
        return;
    }
    X509Certificate[] certs = null;
    ClientModel client = null;
    try {
        certs = provider.getCertificateChain(context.getHttpRequest());
        String client_id = null;
        MediaType mediaType = context.getHttpRequest().getHttpHeaders().getMediaType();
        boolean hasFormData = mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
        MultivaluedMap<String, String> formData = hasFormData ? context.getHttpRequest().getDecodedFormParameters() : null;
        MultivaluedMap<String, String> queryParams = context.getSession().getContext().getUri().getQueryParameters();
        if (formData != null) {
            client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
        }
        if (client_id == null && queryParams != null) {
            client_id = queryParams.getFirst(OAuth2Constants.CLIENT_ID);
        }
        if (client_id == null) {
            client_id = context.getSession().getAttribute("client_id", String.class);
        }
        if (client_id == null) {
            Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Missing client_id parameter");
            context.challenge(challengeResponse);
            return;
        }
        client = context.getRealm().getClientByClientId(client_id);
        if (client == null) {
            context.failure(AuthenticationFlowError.CLIENT_NOT_FOUND, null);
            return;
        }
        context.getEvent().client(client_id);
        context.setClient(client);
        if (!client.isEnabled()) {
            context.failure(AuthenticationFlowError.CLIENT_DISABLED, null);
            return;
        }
    } catch (GeneralSecurityException e) {
        logger.errorf("[X509ClientCertificateAuthenticator:authenticate] Exception: %s", e.getMessage());
        context.attempted();
        return;
    }
    if (certs == null || certs.length == 0) {
        // No x509 client cert, fall through and
        // continue processing the rest of the authentication flow
        logger.debug("[X509ClientCertificateAuthenticator:authenticate] x509 client certificate is not available for mutual SSL.");
        context.attempted();
        return;
    }
    OIDCAdvancedConfigWrapper clientCfg = OIDCAdvancedConfigWrapper.fromClientModel(client);
    String subjectDNRegexp = client.getAttribute(ATTR_SUBJECT_DN);
    if (subjectDNRegexp == null || subjectDNRegexp.length() == 0) {
        logger.errorf("[X509ClientCertificateAuthenticator:authenticate] " + ATTR_SUBJECT_DN + " is null or empty");
        context.attempted();
        return;
    }
    Optional<String> matchedCertificate;
    if (clientCfg.getAllowRegexPatternComparison()) {
        Pattern subjectDNPattern = Pattern.compile(subjectDNRegexp);
        matchedCertificate = Arrays.stream(certs).map(certificate -> certificate.getSubjectDN().getName()).filter(subjectdn -> subjectDNPattern.matcher(subjectdn).matches()).findFirst();
    } else {
        // OIDC/OAuth2 does not use regex comparison as it expects exact DN given in the format according to RFC4514. See RFC8705 for the details.
        // We allow custom OIDs attributes to be "expanded" or not expanded in the given Subject DN
        X500Principal expectedDNPrincipal = new X500Principal(subjectDNRegexp, CUSTOM_OIDS_REVERSED);
        matchedCertificate = Arrays.stream(certs).filter(certificate -> expectedDNPrincipal.getName(X500Principal.RFC2253, CUSTOM_OIDS).equals(certificate.getSubjectX500Principal().getName(X500Principal.RFC2253, CUSTOM_OIDS))).map(certificate -> certificate.getSubjectDN().getName()).findFirst();
    }
    if (!matchedCertificate.isPresent()) {
        // We do quite expensive operation here, so better check the logging level beforehand.
        if (logger.isDebugEnabled()) {
            logger.debug("[X509ClientCertificateAuthenticator:authenticate] Couldn't match any certificate for expected Subject DN '" + subjectDNRegexp + "' with allow regex pattern '" + clientCfg.getAllowRegexPatternComparison() + "'.");
            logger.debug("[X509ClientCertificateAuthenticator:authenticate] Available SubjectDNs: " + Arrays.stream(certs).map(cert -> cert.getSubjectDN().getName()).collect(Collectors.toList()));
        }
        context.attempted();
        return;
    } else {
        logger.debug("[X509ClientCertificateAuthenticator:authenticate] Matched " + matchedCertificate.get() + " certificate.");
    }
    context.success();
}
Also used : ClientModel(org.keycloak.models.ClientModel) X509Certificate(java.security.cert.X509Certificate) Arrays(java.util.Arrays) X500Principal(javax.security.auth.x500.X500Principal) ProviderConfigProperty(org.keycloak.provider.ProviderConfigProperty) HashMap(java.util.HashMap) ServicesLogger(org.keycloak.services.ServicesLogger) ClientAuthenticationFlowContext(org.keycloak.authentication.ClientAuthenticationFlowContext) Function(java.util.function.Function) HashSet(java.util.HashSet) MediaType(javax.ws.rs.core.MediaType) GeneralSecurityException(java.security.GeneralSecurityException) AuthenticationExecutionModel(org.keycloak.models.AuthenticationExecutionModel) OIDCAdvancedConfigWrapper(org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper) Map(java.util.Map) AuthenticationFlowError(org.keycloak.authentication.AuthenticationFlowError) Set(java.util.Set) Collectors(java.util.stream.Collectors) X509ClientCertificateLookup(org.keycloak.services.x509.X509ClientCertificateLookup) MultivaluedMap(javax.ws.rs.core.MultivaluedMap) List(java.util.List) Response(javax.ws.rs.core.Response) OIDCLoginProtocol(org.keycloak.protocol.oidc.OIDCLoginProtocol) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) Collections(java.util.Collections) OAuth2Constants(org.keycloak.OAuth2Constants) Pattern(java.util.regex.Pattern) OIDCAdvancedConfigWrapper(org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper) GeneralSecurityException(java.security.GeneralSecurityException) X509ClientCertificateLookup(org.keycloak.services.x509.X509ClientCertificateLookup) X509Certificate(java.security.cert.X509Certificate) Response(javax.ws.rs.core.Response) ClientModel(org.keycloak.models.ClientModel) MediaType(javax.ws.rs.core.MediaType) X500Principal(javax.security.auth.x500.X500Principal)

Example 2 with X509ClientCertificateLookup

use of org.keycloak.services.x509.X509ClientCertificateLookup in project keycloak by keycloak.

the class AbstractX509ClientCertificateAuthenticator method getCertificateChain.

protected X509Certificate[] getCertificateChain(AuthenticationFlowContext context) {
    try {
        // Get a x509 client certificate
        X509ClientCertificateLookup provider = context.getSession().getProvider(X509ClientCertificateLookup.class);
        if (provider == null) {
            logger.errorv("\"{0}\" Spi is not available, did you forget to update the configuration?", X509ClientCertificateLookup.class);
            return null;
        }
        X509Certificate[] certs = provider.getCertificateChain(context.getHttpRequest());
        if (certs != null) {
            for (X509Certificate cert : certs) {
                logger.tracev("\"{0}\"", cert.getSubjectDN().getName());
            }
        }
        return certs;
    } catch (GeneralSecurityException e) {
        logger.error(e.getMessage(), e);
    }
    return null;
}
Also used : GeneralSecurityException(java.security.GeneralSecurityException) X509ClientCertificateLookup(org.keycloak.services.x509.X509ClientCertificateLookup) X509Certificate(java.security.cert.X509Certificate)

Example 3 with X509ClientCertificateLookup

use of org.keycloak.services.x509.X509ClientCertificateLookup in project keycloak by keycloak.

the class MtlsHoKTokenUtil method getCertificateChain.

private static X509Certificate[] getCertificateChain(HttpRequest request, KeycloakSession session) {
    try {
        // Get a x509 client certificate
        X509ClientCertificateLookup provider = session.getProvider(X509ClientCertificateLookup.class);
        if (provider == null) {
            logger.errorv("\"{0}\" Spi is not available, did you forget to update the configuration?", X509ClientCertificateLookup.class);
            return null;
        }
        X509Certificate[] certs = provider.getCertificateChain(request);
        return certs;
    } catch (GeneralSecurityException e) {
        logger.error(e.getMessage(), e);
    }
    return null;
}
Also used : GeneralSecurityException(java.security.GeneralSecurityException) X509ClientCertificateLookup(org.keycloak.services.x509.X509ClientCertificateLookup) X509Certificate(java.security.cert.X509Certificate)

Aggregations

GeneralSecurityException (java.security.GeneralSecurityException)3 X509Certificate (java.security.cert.X509Certificate)3 X509ClientCertificateLookup (org.keycloak.services.x509.X509ClientCertificateLookup)3 Arrays (java.util.Arrays)1 Collections (java.util.Collections)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Map (java.util.Map)1 Optional (java.util.Optional)1 Set (java.util.Set)1 Function (java.util.function.Function)1 Pattern (java.util.regex.Pattern)1 Collectors (java.util.stream.Collectors)1 X500Principal (javax.security.auth.x500.X500Principal)1 MediaType (javax.ws.rs.core.MediaType)1 MultivaluedMap (javax.ws.rs.core.MultivaluedMap)1 Response (javax.ws.rs.core.Response)1 OAuth2Constants (org.keycloak.OAuth2Constants)1 AuthenticationFlowError (org.keycloak.authentication.AuthenticationFlowError)1