diff --git a/docs/guides/src/main/server/reverseproxy.adoc b/docs/guides/src/main/server/reverseproxy.adoc
index 2924c70d3c2..7d7fc3d3fe0 100644
--- a/docs/guides/src/main/server/reverseproxy.adoc
+++ b/docs/guides/src/main/server/reverseproxy.adoc
@@ -165,6 +165,9 @@ to load additional certificates from headers `CERT_CHAIN_0` to `CERT_CHAIN_9` if
|certificate-chain-length
| The maximum length of the certificate chain.
+
+|trust-proxy-verification
+| Enable trusting NGINX proxy certificate verification, instead of forwarding the certificate to keycloak and verifying it in keycloak.
|===
==== Configuring the NGINX provider
diff --git a/services/src/main/java/org/keycloak/services/x509/NginxProxySslClientCertificateLookupFactory.java b/services/src/main/java/org/keycloak/services/x509/NginxProxySslClientCertificateLookupFactory.java
index 9a4a742637b..b2bdd249c9f 100644
--- a/services/src/main/java/org/keycloak/services/x509/NginxProxySslClientCertificateLookupFactory.java
+++ b/services/src/main/java/org/keycloak/services/x509/NginxProxySslClientCertificateLookupFactory.java
@@ -1,5 +1,7 @@
package org.keycloak.services.x509;
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
/**
@@ -13,12 +15,30 @@ import org.keycloak.models.KeycloakSession;
public class NginxProxySslClientCertificateLookupFactory extends AbstractClientCertificateFromHttpHeadersLookupFactory {
+ private final static Logger logger = Logger.getLogger(NginxProxySslClientCertificateLookupFactory.class);
+
private final static String PROVIDER = "nginx";
+ protected final static String TRUST_PROXY_VERIFICATION = "trust-proxy-verification";
+
+ protected boolean trustProxyVerification = false;
+
+ @Override
+ public void init(Config.Scope config) {
+ super.init(config);
+ trustProxyVerification = config.getBoolean(TRUST_PROXY_VERIFICATION, false);
+ logger.tracev("{0}: ''{1}''", TRUST_PROXY_VERIFICATION, trustProxyVerification);
+ }
+
@Override
public X509ClientCertificateLookup create(KeycloakSession session) {
- return new NginxProxySslClientCertificateLookup(sslClientCertHttpHeader,
- sslChainHttpHeaderPrefix, certificateChainLength, session);
+ if (trustProxyVerification) {
+ return new NginxProxyTrustedClientCertificateLookup(sslClientCertHttpHeader,
+ sslChainHttpHeaderPrefix, certificateChainLength);
+ } else {
+ return new NginxProxySslClientCertificateLookup(sslClientCertHttpHeader,
+ sslChainHttpHeaderPrefix, certificateChainLength, session);
+ }
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/x509/NginxProxyTrustedClientCertificateLookup.java b/services/src/main/java/org/keycloak/services/x509/NginxProxyTrustedClientCertificateLookup.java
new file mode 100644
index 00000000000..3438e4a94ef
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/x509/NginxProxyTrustedClientCertificateLookup.java
@@ -0,0 +1,79 @@
+package org.keycloak.services.x509;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.common.util.PemException;
+import org.keycloak.common.util.PemUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
+
+/**
+ * The NGINX Trusted Provider verify extract end user X.509 certificate sent during TLS mutual authentication,
+ * verifies it against provided CA the and forwarded in an HTTP header along with a new header ssl-client-verify: SUCCESS.
+ *
+ * NGINX configuration must have :
+ *
+ * server {
+ * ...
+ * ssl_client_certificate path-to-trusted-ca.crt;
+ * ssl_verify_client on|optional;
+ * ssl_verify_depth 2;
+ * ...
+ * location / {
+ * ...
+ * proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
+ * ...
+ * }
+ *
+ *
+ * Note that $ssl_client_cert is deprecated, use only $ssl_client_escaped_cert with this implementation
+ *
+ * @author Youssef El Houti
+ * @version $Revision: 1 $
+ * @since 01/09/2022
+ */
+
+public class NginxProxyTrustedClientCertificateLookup extends AbstractClientCertificateFromHttpHeadersLookup {
+
+ private static final Logger log = Logger.getLogger(NginxProxyTrustedClientCertificateLookup.class);
+
+ public NginxProxyTrustedClientCertificateLookup(String sslCientCertHttpHeader,
+ String sslCertChainHttpHeaderPrefix,
+ int certificateChainLength) {
+ super(sslCientCertHttpHeader, sslCertChainHttpHeaderPrefix, certificateChainLength);
+ }
+
+ @Override
+ protected X509Certificate getCertificateFromHttpHeader(HttpRequest request, String httpHeader) throws GeneralSecurityException {
+ X509Certificate certificate = super.getCertificateFromHttpHeader(request, httpHeader);
+ if (certificate == null) {
+ return null;
+ }
+ String validCertificateResult = getHeaderValue(request, "ssl-client-verify");
+ if ("SUCCESS".equals(validCertificateResult)) {
+ return certificate;
+ } else {
+ log.warn("nginx could not verify the certificate: ssl-client-verify: " + validCertificateResult);
+ return null;
+ }
+ }
+
+ @Override
+ protected X509Certificate decodeCertificateFromPem(String pem) throws PemException {
+
+ if (pem == null) {
+ log.warn("End user TLS Certificate is NULL! ");
+ return null;
+ }
+ try {
+ pem = java.net.URLDecoder.decode(pem, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ log.error("Cannot URL decode the end user TLS Certificate : " + pem,e);
+ }
+
+ return PemUtils.decodeCertificate(pem);
+ }
+
+}