From 29656cbcf29c20f538154f8a4280587ec713e74c Mon Sep 17 00:00:00 2001 From: Dmytro Mishchuk Date: Wed, 19 May 2021 11:47:49 +0300 Subject: [PATCH 1/3] added MTLS external validation support --- .../OIDCConfigurationRepresentation.java | 22 +++++++++++++++ .../oidc/OIDCClientRepresentation.java | 18 ++++++++++++ .../mtls/MtlsExtendedValidationProvider.java | 13 +++++++++ ...MtlsExtendedValidationProviderFactory.java | 6 ++++ .../mtls/MtlsExtendedValidationSpi.java | 27 ++++++++++++++++++ .../services/org.keycloak.provider.Spi | 3 +- .../client/X509ClientAuthenticator.java | 28 +++++++++++++++++++ .../oidc/OIDCAdvancedConfigWrapper.java | 18 ++++++++++++ .../protocol/oidc/OIDCConfigAttributes.java | 4 +++ .../oidc/DescriptionConverter.java | 10 +++++++ .../messages/admin-messages_en.properties | 5 ++++ .../admin/resources/js/controllers/clients.js | 26 +++++++++++++++++ .../resources/partials/client-detail.html | 22 +++++++++++++++ 13 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java create mode 100644 server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProviderFactory.java create mode 100644 server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationSpi.java diff --git a/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java b/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java index 593679cb2bba..15b57ec1abd7 100755 --- a/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java +++ b/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java @@ -127,6 +127,12 @@ public class OIDCConfigurationRepresentation { @JsonProperty("tls_client_certificate_bound_access_tokens") private Boolean tlsClientCertificateBoundAccessTokens; + @JsonProperty("tls_client_certificate_extended_verification") + private Boolean tlsClientCertificateExtendedValidation; + + @JsonProperty("tls_client_certificate_extended_verification_impl") + private String tlsClientCertificateExtendedValidationImplementation; + @JsonProperty("revocation_endpoint") private String revocationEndpoint; @@ -445,4 +451,20 @@ public Map getOtherClaims() { public void setOtherClaims(String name, Object value) { otherClaims.put(name, value); } + + public Boolean getTlsClientCertificateExtendedValidation() { + return tlsClientCertificateExtendedValidation; + } + + public void setTlsClientCertificateExtendedValidation(Boolean tlsClientCertificateExtendedValidation) { + this.tlsClientCertificateExtendedValidation = tlsClientCertificateExtendedValidation; + } + + public String getTlsClientCertificateExtendedValidationImplementation() { + return tlsClientCertificateExtendedValidationImplementation; + } + + public void setTlsClientCertificateExtendedValidationImplementation(String tlsClientCertificateExtendedValidationImplementation) { + this.tlsClientCertificateExtendedValidationImplementation = tlsClientCertificateExtendedValidationImplementation; + } } diff --git a/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java b/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java index 395453b28a57..ab401a5130f4 100644 --- a/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java @@ -100,6 +100,9 @@ public class OIDCClientRepresentation { // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.5 private Boolean tls_client_certificate_bound_access_tokens; + private Boolean tls_client_certificate_extended_validation; + private String tls_client_certificate_extended_validation_impl; + private String tls_client_auth_subject_dn; // OIDC Session Management @@ -487,4 +490,19 @@ public void setTlsClientAuthSubjectDn(String tls_client_auth_subject_dn) { this.tls_client_auth_subject_dn = tls_client_auth_subject_dn; } + public Boolean getTls_client_certificate_extended_validation() { + return tls_client_certificate_extended_validation; + } + + public void setTls_client_certificate_extended_validation(Boolean tls_client_certificate_extended_validation) { + this.tls_client_certificate_extended_validation = tls_client_certificate_extended_validation; + } + + public String getTls_client_certificate_extended_validation_impl() { + return tls_client_certificate_extended_validation_impl; + } + + public void setTls_client_certificate_extended_validation_impl(String tls_client_certificate_extended_validation_impl) { + this.tls_client_certificate_extended_validation_impl = tls_client_certificate_extended_validation_impl; + } } diff --git a/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java new file mode 100644 index 000000000000..2de8c75419ac --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java @@ -0,0 +1,13 @@ +package org.keycloak.mtls; + +import org.keycloak.provider.Provider; + +import java.security.cert.X509Certificate; +import java.util.Map; + +public interface MtlsExtendedValidationProvider extends Provider { + + Map parseAdditionalFields(X509Certificate[] certs); + + void performAdditionalValidation(X509Certificate[] certs); +} diff --git a/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProviderFactory.java b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProviderFactory.java new file mode 100644 index 000000000000..1c031f825d57 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProviderFactory.java @@ -0,0 +1,6 @@ +package org.keycloak.mtls; + +import org.keycloak.provider.ProviderFactory; + +public interface MtlsExtendedValidationProviderFactory extends ProviderFactory { +} diff --git a/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationSpi.java b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationSpi.java new file mode 100644 index 000000000000..7f030440bce3 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationSpi.java @@ -0,0 +1,27 @@ +package org.keycloak.mtls; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +public class MtlsExtendedValidationSpi implements Spi { + @Override + public boolean isInternal() { + return false; + } + + @Override + public String getName() { + return "tls-client-extended-validation-impl"; + } + + @Override + public Class getProviderClass() { + return MtlsExtendedValidationProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return MtlsExtendedValidationProviderFactory.class; + } +} diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi index bdef3ed12c29..d4e4d0309bcc 100755 --- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -37,4 +37,5 @@ org.keycloak.locale.LocaleUpdaterSPI org.keycloak.storage.UserStorageProviderSpi org.keycloak.theme.ThemeResourceSpi org.keycloak.theme.ThemeSelectorSpi -org.keycloak.urls.HostnameSpi \ No newline at end of file +org.keycloak.urls.HostnameSpi +org.keycloak.mtls.MtlsExtendedValidationSpi \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java index 399a84680ba9..2fd3a6f8b062 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java @@ -1,18 +1,22 @@ package org.keycloak.authentication.authenticators.client; import org.keycloak.OAuth2Constants; +import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.AuthenticationFlowError; import org.keycloak.authentication.ClientAuthenticationFlowContext; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.ClientModel; +import org.keycloak.mtls.MtlsExtendedValidationProvider; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderFactory; import org.keycloak.services.ServicesLogger; import org.keycloak.services.x509.X509ClientCertificateLookup; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import java.nio.file.ProviderNotFoundException; import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -127,6 +131,17 @@ public void authenticateClient(ClientAuthenticationFlowContext context) { logger.debug("[X509ClientCertificateAuthenticator:authenticate] Matched " + matchedCertificate.get() + " certificate."); } + boolean mtlsExtendedValidationEnabled = Boolean.parseBoolean(client.getAttribute("tls.client.certificate.extended.validation")); + MtlsExtendedValidationProvider mtlsExtendedValidationProvider = getMtlsExtendedValidationProvider(mtlsExtendedValidationEnabled, context, client); + + if (mtlsExtendedValidationEnabled) { + Map additionalDetails = mtlsExtendedValidationProvider.parseAdditionalFields(certs); + additionalDetails.forEach((k,v)->{ + context.getEvent().detail(k, v); + }); + mtlsExtendedValidationProvider.performAdditionalValidation(certs); + } + context.success(); } @@ -179,4 +194,17 @@ public List getConfigProperties() { public String getId() { return PROVIDER_ID; } + + private MtlsExtendedValidationProvider getMtlsExtendedValidationProvider(boolean mtlsExtendedValidationEnabled, ClientAuthenticationFlowContext context, ClientModel client) { + if (!mtlsExtendedValidationEnabled) { + return null; + } else { + String requiredProviderId = client.getAttribute("tls.client.certificate.extended.validation.impl"); + ProviderFactory factory = context.getSession().getKeycloakSessionFactory() + .getProviderFactoriesStream(MtlsExtendedValidationProvider.class) + .filter(f -> f.getId().equals(requiredProviderId)).findFirst() + .orElseThrow(() -> new ProviderNotFoundException("MTLS Extended validation Provider Not Found! Please specify your provider in your Clients Advanced Settings!")); + return factory.create(context.getSession()); + } + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java index c8bfcbdf99d5..fabbf8e1bbd1 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java @@ -131,6 +131,24 @@ public void setUseMtlsHoKToken(boolean useUtlsHokToken) { setAttribute(OIDCConfigAttributes.USE_MTLS_HOK_TOKEN, val); } + public boolean isUseMtlsExtendedValidation() { + String useMtlsExtendedValidation = getAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION); + return Boolean.parseBoolean(useMtlsExtendedValidation); + } + + public void setUseMtlsExtendedValidation(boolean useMtlsExtendedValidation) { + String val = String.valueOf(useMtlsExtendedValidation); + setAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION, val); + } + + public String getMtlsExtendedValidationImpl(){ + return getAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION_IMPL); + } + + public void setMtlsExtendedValidationImpl(String mtlsExtendedValidationImpl){ + setAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION, mtlsExtendedValidationImpl); + } + /** * If true, then Client Credentials Grant generates refresh token and creates user session. This is not per specs, so it is false by default * For the details @see https://tools.ietf.org/html/rfc6749#section-4.4.3 diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCConfigAttributes.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCConfigAttributes.java index 8d6897e04826..5b45be6a6906 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCConfigAttributes.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCConfigAttributes.java @@ -37,6 +37,10 @@ public final class OIDCConfigAttributes { public static final String USE_MTLS_HOK_TOKEN = "tls.client.certificate.bound.access.tokens"; + public static final String USE_MTLS_EXTENDED_VALIDATION = "tls.client.certificate.extended.validation"; + + public static final String USE_MTLS_EXTENDED_VALIDATION_IMPL = "tls.client.certificate.extended.validation.impl"; + public static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "id.token.signed.response.alg"; public static final String ID_TOKEN_ENCRYPTED_RESPONSE_ALG = "id.token.encrypted.response.alg"; diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java index 763a101b3c23..1bcbde768cb5 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java @@ -127,6 +127,12 @@ public static ClientRepresentation toInternal(KeycloakSession session, OIDCClien else configWrapper.setUseMtlsHoKToken(false); } + boolean tlsClientCertificateExtendedValidation = configWrapper.isUseMtlsHokToken()&&clientOIDC.getTls_client_certificate_extended_validation(); + configWrapper.setUseMtlsExtendedValidation(tlsClientCertificateExtendedValidation); + + String tlsClientCertificateExtendedValidationImpl = clientOIDC.getTls_client_certificate_extended_validation_impl(); + configWrapper.setMtlsExtendedValidationImpl(tlsClientCertificateExtendedValidationImpl); + if (clientOIDC.getTlsClientAuthSubjectDn() != null) { configWrapper.setTlsClientAuthSubjectDn(clientOIDC.getTlsClientAuthSubjectDn()); } @@ -246,6 +252,10 @@ public static OIDCClientRepresentation toExternalResponse(KeycloakSession sessio } else { response.setTlsClientCertificateBoundAccessTokens(Boolean.FALSE); } + + response.setTls_client_certificate_extended_validation(config.isUseMtlsExtendedValidation()); + response.setTls_client_certificate_extended_validation_impl(config.getMtlsExtendedValidationImpl()); + if (config.getTlsClientAuthSubjectDn() != null) { response.setTlsClientAuthSubjectDn(config.getTlsClientAuthSubjectDn()); } diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 9d81b34f379c..4be2df408eb8 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -1767,6 +1767,11 @@ advanced-client-settings=Advanced Settings advanced-client-settings.tooltip=Expand this section to configure advanced settings of this client tls-client-certificate-bound-access-tokens=OAuth 2.0 Mutual TLS Certificate Bound Access Tokens Enabled tls-client-certificate-bound-access-tokens.tooltip=This enables support for OAuth 2.0 Mutual TLS Certificate Bound Access Tokens, which means that keycloak bind an access token and a refresh token with a X.509 certificate of a token requesting client exchanged in mutual TLS between keycloak's Token Endpoint and this client. These tokens can be treated as Holder-of-Key tokens instead of bearer tokens. +tls-client-certificate-extended-validation=OAuth 2.0 Mutual TLS Certificate Extended Validation Enabled +tls-client-certificate-extended-validation.tooltip=This enables support from OAuth 2.0 Mutual TLS Certificate Extended Validation, which means that keycloak will perform additional validation and parameters extraction through one of custom extensions. +tls-client-certificate-extended-validation-impl=OAuth 2.0 Mutual TLS Certificate Extended Validation Implementation +tls-client-certificate-extended-validation-impl.tooltip=Select your OAuth 2.0 Mutual TLS Certificate Extended Validation Implementation here + subjectdn=Subject DN subjectdn-tooltip=A regular expression for validating Subject DN in the Client Certificate. Use "(.*?)(?:$)" to match all kind of expressions. diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index ee16d067061d..0691fa927cfd 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -1111,6 +1111,9 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro // KEYCLOAK-6771 Certificate Bound Token // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3 $scope.tlsClientCertificateBoundAccessTokens = false; + $scope.tlsClientCertificateExtendedValidation = false; + $scope.tlsClientCertificateExtendedValidationImpl = client.attributes['tls.client.certificate.extended.validation.impl']; + $scope.tlsClientCertificateExtendedValidationImpls = serverInfo.listProviderIds('tls-client-extended-validation-impl'); $scope.accessTokenLifespan = TimeUnit2.asUnit(client.attributes['access.token.lifespan']); $scope.samlAssertionLifespan = TimeUnit2.asUnit(client.attributes['saml.assertion.lifespan']); @@ -1289,6 +1292,16 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro } } + if ($scope.client.attributes["tls.client.certificate.extended.validation"]) { + $scope.tlsClientCertificateExtendedValidation = $scope.client.attributes["tls.client.certificate.extended.validation"] === "true"; + } + + if ($scope.client.attributes['tls.client.certificate.extended.validation.impl']==null){ + $scope.tlsClientCertificateExtendedValidationImpl = ''; + } else { + $scope.tlsClientCertificateExtendedValidationImpl = $scope.client.attributes['tls.client.certificate.extended.validation.impl']; + } + var useRefreshToken = $scope.client.attributes["client_credentials.use_refresh_token"]; if (useRefreshToken === "true") { $scope.useRefreshTokenForClientCredentialsGrant = true; @@ -1447,6 +1460,10 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro $scope.clientEdit.attributes['pkce.code.challenge.method'] = $scope.pkceCodeChallengeMethod; }; + $scope.changeMtlsExtendedValidationImpl = function() { + $scope.clientEdit.attributes['tls.client.certificate.extended.validation.impl'] = $scope.tlsClientCertificateExtendedValidationImpl; + }; + $scope.$watch(function() { return $location.path(); }, function() { @@ -1672,6 +1689,15 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro $scope.clientEdit.attributes["tls.client.certificate.bound.access.tokens"] = "false"; } + $scope.clientEdit.attributes["tls.client.certificate.extended.validation"] = $scope.tlsClientCertificateExtendedValidation && $scope.tlsClientCertificateBoundAccessTokens === true + ? "true" + : "false"; + + + $scope.clientEdit.attributes["tls.client.certificate.extended.validation.impl"] = $scope.tlsClientCertificateExtendedValidationImpl === 'none' || !$scope.tlsClientCertificateExtendedValidation + ? null + : $scope.tlsClientCertificateExtendedValidationImpl; + // KEYCLOAK-9551 Client Credentials Grant generates refresh token // https://tools.ietf.org/html/rfc6749#section-4.4.3 if ($scope.useRefreshTokenForClientCredentialsGrant === true) { diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index e5cd2d09b7b4..8db584c18db0 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -670,6 +670,28 @@ {{:: 'tls-client-certificate-bound-access-tokens.tooltip' | translate}} +
+ +
+ +
+ {{:: 'tls-client-certificate-extended-validation.tooltip' | translate}} +
+ +
+ +
+
+ +
+
+ {{:: 'tls-client-certificate-extended-validation-impl.tooltip' | translate}} +
+
From 5d41a2c0c3c362329a5bc235eecfb5e6eb3707fc Mon Sep 17 00:00:00 2001 From: Dmytro Mishchuk Date: Fri, 21 May 2021 16:30:31 +0300 Subject: [PATCH 2/3] fixed some comments made by @tnorimat --- .../OIDCConfigurationRepresentation.java | 22 ------------------- .../oidc/OIDCClientRepresentation.java | 18 --------------- .../client/X509ClientAuthenticator.java | 8 +++---- .../oidc/OIDCAdvancedConfigWrapper.java | 18 --------------- .../oidc/DescriptionConverter.java | 10 --------- 5 files changed, 4 insertions(+), 72 deletions(-) diff --git a/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java b/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java index 15b57ec1abd7..593679cb2bba 100755 --- a/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java +++ b/core/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java @@ -127,12 +127,6 @@ public class OIDCConfigurationRepresentation { @JsonProperty("tls_client_certificate_bound_access_tokens") private Boolean tlsClientCertificateBoundAccessTokens; - @JsonProperty("tls_client_certificate_extended_verification") - private Boolean tlsClientCertificateExtendedValidation; - - @JsonProperty("tls_client_certificate_extended_verification_impl") - private String tlsClientCertificateExtendedValidationImplementation; - @JsonProperty("revocation_endpoint") private String revocationEndpoint; @@ -451,20 +445,4 @@ public Map getOtherClaims() { public void setOtherClaims(String name, Object value) { otherClaims.put(name, value); } - - public Boolean getTlsClientCertificateExtendedValidation() { - return tlsClientCertificateExtendedValidation; - } - - public void setTlsClientCertificateExtendedValidation(Boolean tlsClientCertificateExtendedValidation) { - this.tlsClientCertificateExtendedValidation = tlsClientCertificateExtendedValidation; - } - - public String getTlsClientCertificateExtendedValidationImplementation() { - return tlsClientCertificateExtendedValidationImplementation; - } - - public void setTlsClientCertificateExtendedValidationImplementation(String tlsClientCertificateExtendedValidationImplementation) { - this.tlsClientCertificateExtendedValidationImplementation = tlsClientCertificateExtendedValidationImplementation; - } } diff --git a/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java b/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java index ab401a5130f4..395453b28a57 100644 --- a/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java @@ -100,9 +100,6 @@ public class OIDCClientRepresentation { // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.5 private Boolean tls_client_certificate_bound_access_tokens; - private Boolean tls_client_certificate_extended_validation; - private String tls_client_certificate_extended_validation_impl; - private String tls_client_auth_subject_dn; // OIDC Session Management @@ -490,19 +487,4 @@ public void setTlsClientAuthSubjectDn(String tls_client_auth_subject_dn) { this.tls_client_auth_subject_dn = tls_client_auth_subject_dn; } - public Boolean getTls_client_certificate_extended_validation() { - return tls_client_certificate_extended_validation; - } - - public void setTls_client_certificate_extended_validation(Boolean tls_client_certificate_extended_validation) { - this.tls_client_certificate_extended_validation = tls_client_certificate_extended_validation; - } - - public String getTls_client_certificate_extended_validation_impl() { - return tls_client_certificate_extended_validation_impl; - } - - public void setTls_client_certificate_extended_validation_impl(String tls_client_certificate_extended_validation_impl) { - this.tls_client_certificate_extended_validation_impl = tls_client_certificate_extended_validation_impl; - } } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java index 2fd3a6f8b062..ff474d7130db 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java @@ -7,6 +7,7 @@ import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.ClientModel; import org.keycloak.mtls.MtlsExtendedValidationProvider; +import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderFactory; @@ -131,10 +132,9 @@ public void authenticateClient(ClientAuthenticationFlowContext context) { logger.debug("[X509ClientCertificateAuthenticator:authenticate] Matched " + matchedCertificate.get() + " certificate."); } - boolean mtlsExtendedValidationEnabled = Boolean.parseBoolean(client.getAttribute("tls.client.certificate.extended.validation")); - MtlsExtendedValidationProvider mtlsExtendedValidationProvider = getMtlsExtendedValidationProvider(mtlsExtendedValidationEnabled, context, client); - + boolean mtlsExtendedValidationEnabled = Boolean.parseBoolean(client.getAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION)); if (mtlsExtendedValidationEnabled) { + MtlsExtendedValidationProvider mtlsExtendedValidationProvider = getMtlsExtendedValidationProvider(mtlsExtendedValidationEnabled, context, client); Map additionalDetails = mtlsExtendedValidationProvider.parseAdditionalFields(certs); additionalDetails.forEach((k,v)->{ context.getEvent().detail(k, v); @@ -199,7 +199,7 @@ private MtlsExtendedValidationProvider getMtlsExtendedValidationProvider(boolean if (!mtlsExtendedValidationEnabled) { return null; } else { - String requiredProviderId = client.getAttribute("tls.client.certificate.extended.validation.impl"); + String requiredProviderId = client.getAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION_IMPL); ProviderFactory factory = context.getSession().getKeycloakSessionFactory() .getProviderFactoriesStream(MtlsExtendedValidationProvider.class) .filter(f -> f.getId().equals(requiredProviderId)).findFirst() diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java index fabbf8e1bbd1..c8bfcbdf99d5 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java @@ -131,24 +131,6 @@ public void setUseMtlsHoKToken(boolean useUtlsHokToken) { setAttribute(OIDCConfigAttributes.USE_MTLS_HOK_TOKEN, val); } - public boolean isUseMtlsExtendedValidation() { - String useMtlsExtendedValidation = getAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION); - return Boolean.parseBoolean(useMtlsExtendedValidation); - } - - public void setUseMtlsExtendedValidation(boolean useMtlsExtendedValidation) { - String val = String.valueOf(useMtlsExtendedValidation); - setAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION, val); - } - - public String getMtlsExtendedValidationImpl(){ - return getAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION_IMPL); - } - - public void setMtlsExtendedValidationImpl(String mtlsExtendedValidationImpl){ - setAttribute(OIDCConfigAttributes.USE_MTLS_EXTENDED_VALIDATION, mtlsExtendedValidationImpl); - } - /** * If true, then Client Credentials Grant generates refresh token and creates user session. This is not per specs, so it is false by default * For the details @see https://tools.ietf.org/html/rfc6749#section-4.4.3 diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java index 1bcbde768cb5..763a101b3c23 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java @@ -127,12 +127,6 @@ public static ClientRepresentation toInternal(KeycloakSession session, OIDCClien else configWrapper.setUseMtlsHoKToken(false); } - boolean tlsClientCertificateExtendedValidation = configWrapper.isUseMtlsHokToken()&&clientOIDC.getTls_client_certificate_extended_validation(); - configWrapper.setUseMtlsExtendedValidation(tlsClientCertificateExtendedValidation); - - String tlsClientCertificateExtendedValidationImpl = clientOIDC.getTls_client_certificate_extended_validation_impl(); - configWrapper.setMtlsExtendedValidationImpl(tlsClientCertificateExtendedValidationImpl); - if (clientOIDC.getTlsClientAuthSubjectDn() != null) { configWrapper.setTlsClientAuthSubjectDn(clientOIDC.getTlsClientAuthSubjectDn()); } @@ -252,10 +246,6 @@ public static OIDCClientRepresentation toExternalResponse(KeycloakSession sessio } else { response.setTlsClientCertificateBoundAccessTokens(Boolean.FALSE); } - - response.setTls_client_certificate_extended_validation(config.isUseMtlsExtendedValidation()); - response.setTls_client_certificate_extended_validation_impl(config.getMtlsExtendedValidationImpl()); - if (config.getTlsClientAuthSubjectDn() != null) { response.setTlsClientAuthSubjectDn(config.getTlsClientAuthSubjectDn()); } From 04e9321e00807bf3bbb795185f2963f3bfd5c645 Mon Sep 17 00:00:00 2001 From: Dmytro Mishchuk Date: Tue, 25 May 2021 14:36:53 +0300 Subject: [PATCH 3/3] added MtlsValidationException and respective exception handling logics --- .../mtls/MtlsExtendedValidationProvider.java | 2 +- .../mtls/MtlsValidationException.java | 20 +++++++++++++++++++ .../client/X509ClientAuthenticator.java | 9 ++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 server-spi/src/main/java/org/keycloak/mtls/MtlsValidationException.java diff --git a/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java index 2de8c75419ac..f3a56dea6779 100644 --- a/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java +++ b/server-spi/src/main/java/org/keycloak/mtls/MtlsExtendedValidationProvider.java @@ -9,5 +9,5 @@ public interface MtlsExtendedValidationProvider extends Provider { Map parseAdditionalFields(X509Certificate[] certs); - void performAdditionalValidation(X509Certificate[] certs); + void performAdditionalValidation(X509Certificate[] certs) throws MtlsValidationException; } diff --git a/server-spi/src/main/java/org/keycloak/mtls/MtlsValidationException.java b/server-spi/src/main/java/org/keycloak/mtls/MtlsValidationException.java new file mode 100644 index 000000000000..2b845c77ea93 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/mtls/MtlsValidationException.java @@ -0,0 +1,20 @@ +package org.keycloak.mtls; + +public class MtlsValidationException extends Exception { + private String validationFailureCause; + public MtlsValidationException() { + } + + public MtlsValidationException(String message, String validationFailureCause) { + super(message); + this.validationFailureCause = validationFailureCause; + } + + public String getValidationFailureCause() { + return validationFailureCause; + } + + public void setValidationFailureCause(String validationFailureCause) { + this.validationFailureCause = validationFailureCause; + } +} diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java index ff474d7130db..7af896ae8f85 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java @@ -7,6 +7,7 @@ import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.ClientModel; import org.keycloak.mtls.MtlsExtendedValidationProvider; +import org.keycloak.mtls.MtlsValidationException; import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; @@ -139,7 +140,13 @@ public void authenticateClient(ClientAuthenticationFlowContext context) { additionalDetails.forEach((k,v)->{ context.getEvent().detail(k, v); }); - mtlsExtendedValidationProvider.performAdditionalValidation(certs); + try { + mtlsExtendedValidationProvider.performAdditionalValidation(certs); + } catch (MtlsValidationException e) { + logger.debug("[X509ClientCertificateAuthenticator:authenticate] There were errors during extended certificate validation: "+e.getValidationFailureCause()); + context.attempted(); + return; + } } context.success();