diff --git a/common/src/main/java/org/keycloak/common/util/OCSPUtils.java b/common/src/main/java/org/keycloak/common/util/OCSPUtils.java
index 9dedec7136..6a6e00c404 100644
--- a/common/src/main/java/org/keycloak/common/util/OCSPUtils.java
+++ b/common/src/main/java/org/keycloak/common/util/OCSPUtils.java
@@ -378,7 +378,7 @@ public final class OCSPUtils {
}
if (signingCert != null) {
if (signingCert.equals(issuerCertificate)) {
- logger.log(Level.INFO, "OCSP response is signed by the target\'s Issuing CA");
+ logger.log(Level.INFO, "OCSP response is signed by the target''s Issuing CA");
} else if (responderCertificate != null && signingCert.equals(responderCertificate)) {
// https://www.ietf.org/rfc/rfc2560.txt
// 2.6 OCSP Signature Authority Delegation
@@ -390,7 +390,7 @@ public final class OCSPUtils {
// extension and is issued by the CA that issued the certificate in
// question."
if (!signingCert.getIssuerX500Principal().equals(issuerCertificate.getSubjectX500Principal())) {
- logger.log(Level.INFO, "Signer certificate's Issuer: {0}\nIssuer certificate's Subject: {1}",
+ logger.log(Level.INFO, "Signer certificate''s Issuer: {0}\nIssuer certificate''s Subject: {1}",
new Object[] {signingCert.getIssuerX500Principal().getName(), issuerCertificate.getSubjectX500Principal().getName()});
throw new CertPathValidatorException("Responder\'s certificate is not authorized to sign OCSP responses");
}
@@ -401,7 +401,7 @@ public final class OCSPUtils {
throw new CertPathValidatorException("Responder\'s certificate not valid for signing OCSP responses");
}
} catch (CertificateParsingException e) {
- logger.log(Level.FINE, "Failed to get certificate's extended key usage extension\n{0}", e.getMessage());
+ logger.log(Level.FINE, "Failed to get certificate''s extended key usage extension\n{0}", e.getMessage());
}
if (date == null) {
signingCert.checkValidity();
diff --git a/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java b/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
index 41f7430836..9b470ecacc 100755
--- a/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
+++ b/server-spi/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
@@ -52,6 +52,11 @@ public class ProviderConfigProperty {
public static final String CLIENT_LIST_TYPE="ClientList";
public static final String PASSWORD="Password";
+ /**
+ * textarea field
+ */
+ public static final String TEXT_TYPE="Text";
+
protected String name;
protected String label;
protected String helpText;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java
index 742454ed37..e58c2a4292 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java
@@ -54,6 +54,7 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth
public static final String ENABLE_CRLDP = "x509-cert-auth.crldp-checking-enabled";
public static final String CRL_RELATIVE_PATH = "x509-cert-auth.crl-relative-path";
public static final String OCSPRESPONDER_URI = "x509-cert-auth.ocsp-responder-uri";
+ public static final String OCSPRESPONDER_CERTIFICATE = "x509-cert-auth.ocsp-responder-certificate";
public static final String MAPPING_SOURCE_SELECTION = "x509-cert-auth.mapping-source-selection";
public static final String MAPPING_SOURCE_CERT_SUBJECTDN = "Match SubjectDN using regular expression";
public static final String MAPPING_SOURCE_CERT_SUBJECTDN_EMAIL = "Subject's e-mail";
@@ -93,6 +94,7 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth
.cRLDPEnabled(config.getCRLDistributionPointEnabled())
.cRLrelativePath(config.getCRLRelativePath())
.oCSPEnabled(config.getOCSPEnabled())
+ .oCSPResponseCertificate(config.getOCSPResponderCertificate())
.oCSPResponderURI(config.getOCSPResponder());
}
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java
index 29af551a75..03e8a33434 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticatorFactory.java
@@ -48,6 +48,7 @@ import static org.keycloak.authentication.authenticators.x509.AbstractX509Client
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SUBJECTDN_CN;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_CERT_SUBJECTDN_EMAIL;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.MAPPING_SOURCE_SELECTION;
+import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.OCSPRESPONDER_CERTIFICATE;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.OCSPRESPONDER_URI;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.REGULAR_EXPRESSION;
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.USERNAME_EMAIL_MAPPER;
@@ -55,6 +56,7 @@ import static org.keycloak.authentication.authenticators.x509.AbstractX509Client
import static org.keycloak.authentication.authenticators.x509.AbstractX509ClientCertificateAuthenticator.USER_MAPPER_SELECTION;
import static org.keycloak.provider.ProviderConfigProperty.BOOLEAN_TYPE;
import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
+import static org.keycloak.provider.ProviderConfigProperty.TEXT_TYPE;
/**
* @author Peter Nalyvayko
@@ -155,6 +157,12 @@ public abstract class AbstractX509ClientCertificateAuthenticatorFactory implemen
ocspResponderUri.setLabel("OCSP Responder Uri");
ocspResponderUri.setHelpText("Clients use OCSP Responder Uri to check certificate revocation status.");
+ ProviderConfigProperty ocspResponderCert = new ProviderConfigProperty();
+ ocspResponderCert.setType(TEXT_TYPE);
+ ocspResponderCert.setName(OCSPRESPONDER_CERTIFICATE);
+ ocspResponderCert.setLabel("OCSP Responder Certificate");
+ ocspResponderCert.setHelpText("Optional certificate used by the responder to sign the responses. The certificate should be in PEM format without BEGIN and END tags. It is only used if the OCSP Responder URI is set. By default, the certificate of the OCSP responder is that of the issuer of the certificate being validated or one with the OCSPSigning extension and also issued by the same CA. This option identifies the certificate of the OCSP responder when the defaults do not apply.");
+
ProviderConfigProperty keyUsage = new ProviderConfigProperty();
keyUsage.setType(STRING_TYPE);
keyUsage.setName(CERTIFICATE_KEY_USAGE);
@@ -182,6 +190,7 @@ public abstract class AbstractX509ClientCertificateAuthenticatorFactory implemen
cRLRelativePath,
oCspCheckingEnabled,
ocspResponderUri,
+ ocspResponderCert,
keyUsage,
extendedKeyUsage,
identityConfirmationPageDisallowed);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java
index 1b9143f7bf..1b2aa80b69 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java
@@ -51,6 +51,8 @@ import java.util.List;
import java.util.Set;
import java.util.LinkedList;
import java.util.ArrayList;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
/**
* @author Peter Nalyvayko
@@ -149,8 +151,11 @@ public class CertificateValidator {
public static class BouncyCastleOCSPChecker extends OCSPChecker {
private final String responderUri;
- BouncyCastleOCSPChecker(String responderUri) {
+ private final X509Certificate responderCert;
+
+ BouncyCastleOCSPChecker(String responderUri, X509Certificate responderCert) {
this.responderUri = responderUri;
+ this.responderCert = responderCert;
}
@Override
@@ -177,12 +182,13 @@ public class CertificateValidator {
String message = String.format("Unable to check certificate revocation status using OCSP.\n%s", e.getMessage());
throw new CertPathValidatorException(message, e);
}
- logger.tracef("Responder URI \"%s\" will be used to verify revocation status of the certificate using OCSP", uri.toString());
+ logger.tracef("Responder URI \"%s\" will be used to verify revocation status of the certificate using OCSP with responderCert=%s",
+ uri.toString(), responderCert);
// Obtains the revocation status of a certificate using OCSP.
// OCSP responder's certificate is assumed to be the issuer's certificate
// certificate.
// responderUri overrides the contents (if any) of the certificate's AIA extension
- ocspRevocationStatus = OCSPUtils.check(cert, issuerCertificate, uri, null, null);
+ ocspRevocationStatus = OCSPUtils.check(cert, issuerCertificate, uri, responderCert, null);
}
return ocspRevocationStatus;
}
@@ -529,6 +535,7 @@ public class CertificateValidator {
CRLLoaderImpl _crlLoader;
boolean _ocspEnabled;
String _responderUri;
+ X509Certificate _responderCert;
public CertificateValidatorBuilder() {
_extendedKeyUsage = new LinkedList<>();
@@ -675,6 +682,21 @@ public class CertificateValidator {
}
public class GotOCSP {
+ public GotOCSP oCSPResponseCertificate(String responderCert) {
+ if (responderCert != null && !responderCert.isEmpty()) {
+ try {
+ _responderCert = XMLSignatureUtil.getX509CertificateFromKeyInfoString(responderCert);
+ _responderCert.checkValidity();
+ } catch(CertificateException e) {
+ logger.warnf("Ignoring invalid certificate: %s", _responderCert);
+ _responderCert = null;
+ } catch (ProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new GotOCSP();
+ }
+
public CertificateValidatorBuilder oCSPResponderURI(String responderURI) {
_responderUri = responderURI;
return _parent;
@@ -699,7 +721,8 @@ public class CertificateValidator {
_crlLoader = new CRLFileLoader("");
}
return new CertificateValidator(certs, _keyUsageBits, _extendedKeyUsage,
- _crlCheckingEnabled, _crldpEnabled, _crlLoader, _ocspEnabled, new BouncyCastleOCSPChecker(_responderUri));
+ _crlCheckingEnabled, _crldpEnabled, _crlLoader, _ocspEnabled,
+ new BouncyCastleOCSPChecker(_responderUri, _responderCert));
}
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java
index a738f42100..f5373744cf 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509AuthenticatorConfigModel.java
@@ -142,6 +142,19 @@ public class X509AuthenticatorConfigModel extends AuthenticatorConfigModel {
return this;
}
+ public String getOCSPResponderCertificate() {
+ return getConfig().getOrDefault(OCSPRESPONDER_CERTIFICATE, null);
+ }
+
+ public X509AuthenticatorConfigModel setOCSPResponderCertificate(String responderCert) {
+ if (responderCert != null) {
+ getConfig().put(OCSPRESPONDER_CERTIFICATE, responderCert);
+ } else {
+ getConfig().remove(OCSPRESPONDER_CERTIFICATE);
+ }
+ return this;
+ }
+
public MappingSourceType getMappingSourceType() {
return MappingSourceType.parse(getConfig().getOrDefault(MAPPING_SOURCE_SELECTION, MAPPING_SOURCE_CERT_SUBJECTDN));
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/OcspHandler.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/OcspHandler.java
index 4f66429a6a..0a4fe7ceb7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/OcspHandler.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/OcspHandler.java
@@ -68,9 +68,13 @@ import io.undertow.util.Headers;
final class OcspHandler implements HttpHandler {
- private static final String OCSP_RESPONDER_CERT_PATH = "/client-auth-test/intermediate-ca.crt";
+ // certificates created by the same ca than the client certs
+ public static final String OCSP_RESPONDER_CERT_PATH = "/client-auth-test/intermediate-ca.crt";
+ public static final String OCSP_RESPONDER_KEYPAIR_PATH = "/client-auth-test/intermediate-ca.key";
- private static final String OCSP_RESPONDER_KEYPAIR_PATH = "/client-auth-test/intermediate-ca.key";
+ // certificates specific for responderCert
+ public static final String OCSP_RESPONDER_CERT_PATH_SPECIFIC = "/client-auth-test/intermediate-ca-2.crt";
+ public static final String OCSP_RESPONDER_KEYPAIR_PATH_SPECIFIC = "/client-auth-test/intermediate-ca-2.key";
// add any certificates that the OCSP responder needs to know about in the tests here
private static final Map REVOKED_CERTIFICATES_STATUS = ImmutableMap
@@ -82,9 +86,10 @@ final class OcspHandler implements HttpHandler {
private final AsymmetricKeyParameter privateKey;
- OcspHandler() throws OperatorCreationException, GeneralSecurityException, IOException {
+ public OcspHandler(String responderCertPath, String responderKeyPath)
+ throws OperatorCreationException, GeneralSecurityException, IOException {
final Certificate certificate = CertificateFactory.getInstance("X509")
- .generateCertificate(X509OCSPResponderTest.class.getResourceAsStream(OCSP_RESPONDER_CERT_PATH));
+ .generateCertificate(X509OCSPResponderTest.class.getResourceAsStream(responderCertPath));
chain = new X509CertificateHolder[] {new X509CertificateHolder(certificate.getEncoded())};
@@ -92,7 +97,7 @@ final class OcspHandler implements HttpHandler {
subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey);
- final InputStream keyPairStream = X509OCSPResponderTest.class.getResourceAsStream(OCSP_RESPONDER_KEYPAIR_PATH);
+ final InputStream keyPairStream = X509OCSPResponderTest.class.getResourceAsStream(responderKeyPath);
try (final PEMParser keyPairReader = new PEMParser(new InputStreamReader(keyPairStream))) {
final PEMKeyPair keyPairPem = (PEMKeyPair) keyPairReader.readObject();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderSpecificCertTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderSpecificCertTest.java
new file mode 100644
index 0000000000..330b6dec89
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderSpecificCertTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2018 Analytical Graphics, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.keycloak.testsuite.x509;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel;
+import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
+import org.keycloak.testsuite.util.OAuthClient;
+
+import javax.ws.rs.core.Response;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.IdentityMapperType.USERNAME_EMAIL;
+import static org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.MappingSourceType.SUBJECTDN_EMAIL;
+
+import io.undertow.Undertow;
+import io.undertow.server.handlers.BlockingHandler;
+
+/**
+ * Verifies Certificate revocation using OCSP responder but specifying specific
+ * responder cert.
+ * The tests rely on an OCSP responder service listening
+ * for OCSP requests on http://localhost:8888
+ * @author rmartinc
+ * @version $Revision: 1 $
+ * @since 11/2/2016
+ */
+public class X509OCSPResponderSpecificCertTest extends AbstractX509AuthenticationTest {
+
+ private static final String OCSP_RESPONDER_HOST = "localhost";
+
+ private static final int OCSP_RESPONDER_PORT = 8888;
+
+ private Undertow ocspResponder;
+
+ @Test
+ public void loginFailedInvalidResponderOnOCSPResponderRevocationCheck() throws Exception {
+ X509AuthenticatorConfigModel config = new X509AuthenticatorConfigModel()
+ .setOCSPEnabled(true)
+ .setMappingSourceType(SUBJECTDN_EMAIL)
+ .setUserIdentityMapperType(USERNAME_EMAIL);
+ AuthenticatorConfigRepresentation cfg = newConfig("x509-directgrant-config", config.getConfig());
+ String cfgId = createConfig(directGrantExecution.getId(), cfg);
+ Assert.assertNotNull(cfgId);
+
+ oauth.clientId("resource-owner");
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "", "", null);
+
+ assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), response.getStatusCode());
+ assertEquals("invalid_request", response.getError());
+
+ Assert.assertThat(response.getErrorDescription(), containsString("Responder's certificate is not authorized to sign OCSP responses"));
+ }
+
+ @Test
+ public void loginFailedOnOCSPResponderRevocationCheck() throws Exception {
+ X509AuthenticatorConfigModel config = new X509AuthenticatorConfigModel()
+ .setOCSPEnabled(true)
+ .setOCSPResponder("http://" + OCSP_RESPONDER_HOST + ":" + OCSP_RESPONDER_PORT + "/oscp")
+ .setOCSPResponderCertificate(
+ "MIIDSDCCAjACCQDutBlh01xKxDANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJV\n"
+ + "UzELMAkGA1UECBMCTUExETAPBgNVBAcTCFdlc3R3b3JkMRAwDgYDVQQKEwdSZWQg\n"
+ + "SGF0MREwDwYDVQQLEwhLZXljbG9hazESMBAGA1UEAxMJbG9jYWxob3N0MCAXDTE4\n"
+ + "MTEyOTE1MzYxNFoYDzMwMTgwNDAxMTUzNjE0WjBkMQswCQYDVQQGEwJVUzELMAkG\n"
+ + "A1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjEQMA4GA1UECgwHUmVkIEhhdDERMA8G\n"
+ + "A1UECwwIS2V5Y2xvYWsxEjAQBgNVBAMMCUtleWNsb2FrMjCCASIwDQYJKoZIhvcN\n"
+ + "AQEBBQADggEPADCCAQoCggEBALxnRdlqot+p3fAZ8BPt/ZeytVy2ZFXd7zG8jVCu\n"
+ + "j/u/IqrN9fE7esdaiZYEwMvaTPKG7pAxb5NlRgWsE8UfNNN9a0GCp3wPJsmj4Lfx\n"
+ + "K7LmH9QLtq+K7Ap5UGXXRNU0BZqcDMznMaIz04N4DimX5uGAwFQCy/NM5yUP2iOa\n"
+ + "dPlB6ECpLavHDT09rMSVl5RgLQLx/TeX7pT4IW7kbpdTVI02rzE90O72riK61c6P\n"
+ + "Q9Zb6bGSaNZwfNGIVQ8u6AVimLJx66p3BNP+3kEfg7xvXkw6UcaXM5LlMVcxi7cr\n"
+ + "Se1k2UR95gPwJC1AVVPUPqHVb3Ix/wLl7GGhHcuBLPODF0cCAwEAATANBgkqhkiG\n"
+ + "9w0BAQsFAAOCAQEADX2znEyqJZHGWLazrSHFn9Rn1mREqH+OCRq38ymz3tCNyVhs\n"
+ + "OTSO1t6Fo1PP4RvlxB6gd4BYH7/cSCsO00s/OjPf8ptqz59TQAmCIM0+dwQuyxKO\n"
+ + "gq55nWpy5gbqf/zqQiWsMXW5nkMVEMUvf1qbKx6xYP61B83vZh+t65LZh7meG6S5\n"
+ + "B5qT6nDGKN7C8AHuxHHJjpgvYL8kNb47fASTYdHzW57Yi92NrkmAq4PCEt6FQTkX\n"
+ + "WybC/Il6hS0jPdR2ExNV9ykKJrNGGhiwg3C8sf97/Kf+qQgRK8wQIdT88g81aJaG\n"
+ + "qpwfJXd9AZO7DdDJdZ75lR9N1YSnhSq3Ur6IJg==")
+ .setMappingSourceType(SUBJECTDN_EMAIL)
+ .setUserIdentityMapperType(USERNAME_EMAIL);
+ AuthenticatorConfigRepresentation cfg = newConfig("x509-directgrant-config", config.getConfig());
+ String cfgId = createConfig(directGrantExecution.getId(), cfg);
+ Assert.assertNotNull(cfgId);
+
+ oauth.clientId("resource-owner");
+ OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "", "", null);
+
+ assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), response.getStatusCode());
+ assertEquals("invalid_request", response.getError());
+
+ Assert.assertThat(response.getErrorDescription(), containsString("Certificate's been revoked."));
+ }
+
+ @Before
+ public void startOCSPResponder() throws Exception {
+ ocspResponder = Undertow.builder().addHttpListener(OCSP_RESPONDER_PORT, OCSP_RESPONDER_HOST)
+ .setHandler(new BlockingHandler(new OcspHandler(
+ OcspHandler.OCSP_RESPONDER_CERT_PATH_SPECIFIC,
+ OcspHandler.OCSP_RESPONDER_KEYPAIR_PATH_SPECIFIC))
+ ).build();
+
+ ocspResponder.start();
+ }
+
+ @After
+ public void stopOCSPResponder() {
+ ocspResponder.stop();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderTest.java
index f44fc64bcb..96146622cd 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509OCSPResponderTest.java
@@ -76,7 +76,9 @@ public class X509OCSPResponderTest extends AbstractX509AuthenticationTest {
@Before
public void startOCSPResponder() throws Exception {
ocspResponder = Undertow.builder().addHttpListener(OCSP_RESPONDER_PORT, OCSP_RESPONDER_HOST)
- .setHandler(new BlockingHandler(new OcspHandler())).build();
+ .setHandler(new BlockingHandler(
+ new OcspHandler(OcspHandler.OCSP_RESPONDER_CERT_PATH, OcspHandler.OCSP_RESPONDER_KEYPAIR_PATH))
+ ).build();
ocspResponder.start();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/intermediate-ca-2.crt b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/intermediate-ca-2.crt
new file mode 100644
index 0000000000..a26da8fe40
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/intermediate-ca-2.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjACCQDutBlh01xKxDANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJV
+UzELMAkGA1UECBMCTUExETAPBgNVBAcTCFdlc3R3b3JkMRAwDgYDVQQKEwdSZWQg
+SGF0MREwDwYDVQQLEwhLZXljbG9hazESMBAGA1UEAxMJbG9jYWxob3N0MCAXDTE4
+MTEyOTE1MzYxNFoYDzMwMTgwNDAxMTUzNjE0WjBkMQswCQYDVQQGEwJVUzELMAkG
+A1UECAwCTUExDzANBgNVBAcMBkJvc3RvbjEQMA4GA1UECgwHUmVkIEhhdDERMA8G
+A1UECwwIS2V5Y2xvYWsxEjAQBgNVBAMMCUtleWNsb2FrMjCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALxnRdlqot+p3fAZ8BPt/ZeytVy2ZFXd7zG8jVCu
+j/u/IqrN9fE7esdaiZYEwMvaTPKG7pAxb5NlRgWsE8UfNNN9a0GCp3wPJsmj4Lfx
+K7LmH9QLtq+K7Ap5UGXXRNU0BZqcDMznMaIz04N4DimX5uGAwFQCy/NM5yUP2iOa
+dPlB6ECpLavHDT09rMSVl5RgLQLx/TeX7pT4IW7kbpdTVI02rzE90O72riK61c6P
+Q9Zb6bGSaNZwfNGIVQ8u6AVimLJx66p3BNP+3kEfg7xvXkw6UcaXM5LlMVcxi7cr
+Se1k2UR95gPwJC1AVVPUPqHVb3Ix/wLl7GGhHcuBLPODF0cCAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEADX2znEyqJZHGWLazrSHFn9Rn1mREqH+OCRq38ymz3tCNyVhs
+OTSO1t6Fo1PP4RvlxB6gd4BYH7/cSCsO00s/OjPf8ptqz59TQAmCIM0+dwQuyxKO
+gq55nWpy5gbqf/zqQiWsMXW5nkMVEMUvf1qbKx6xYP61B83vZh+t65LZh7meG6S5
+B5qT6nDGKN7C8AHuxHHJjpgvYL8kNb47fASTYdHzW57Yi92NrkmAq4PCEt6FQTkX
+WybC/Il6hS0jPdR2ExNV9ykKJrNGGhiwg3C8sf97/Kf+qQgRK8wQIdT88g81aJaG
+qpwfJXd9AZO7DdDJdZ75lR9N1YSnhSq3Ur6IJg==
+-----END CERTIFICATE-----
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/intermediate-ca-2.key b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/intermediate-ca-2.key
new file mode 100644
index 0000000000..0ed58218c9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/intermediate-ca-2.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEAvGdF2Wqi36nd8BnwE+39l7K1XLZkVd3vMbyNUK6P+78iqs31
+8Tt6x1qJlgTAy9pM8obukDFvk2VGBawTxR80031rQYKnfA8myaPgt/ErsuYf1Au2
+r4rsCnlQZddE1TQFmpwMzOcxojPTg3gOKZfm4YDAVALL80znJQ/aI5p0+UHoQKkt
+q8cNPT2sxJWXlGAtAvH9N5fulPghbuRul1NUjTavMT3Q7vauIrrVzo9D1lvpsZJo
+1nB80YhVDy7oBWKYsnHrqncE0/7eQR+DvG9eTDpRxpczkuUxVzGLtytJ7WTZRH3m
+A/AkLUBVU9Q+odVvcjH/AuXsYaEdy4Es84MXRwIDAQABAoIBAQCTcxPUm8OK3qvm
+zLX7MwiEoAWW4NxX45DfUR0cJvJi6W0dVoIEYTOvL+l8Mo4dPOV57iZpTXdwWRNb
+Pxhd7xVt67t0kue3jTgjGZG3BBXaNoZ0cxJwAn7Hl7hXbAnf/o3gOf8+ojZTJtKE
+EwKqhMrac+SSG3o5GVfRxr1HngusQxJvT5amKoG01jILMPJ5ihXKnxKLSxUQL18Z
+UsCYiWBtcFpp06frjqke44E06YTDoUMMzuQJ1sLmJvbVkNgJNSZVAu719TZ5MNcu
+4ioIxzpHVnjfSsDwzyO8/NV72WTwXqHppluGVtPU3iOECk3KUflQVe7qxDUmw0e7
+vrIe9o5JAoGBAPB+Dl/ObNtMgAVE4rEGl4pUIumXCblc/odGdWxLQwCmtPYborTU
+IZWLDmZ426P9iIhd95a/2T4XkioMcwbz/I8HKJn8dQ6yRSpHrUhruQFxK0FXIWMG
+6e3BM9OE+0hyCiJysZ6SA8DEiyw33xHGyMoeFIRjisxfng+JIHC4J7gtAoGBAMiN
+Wq7uTSnz74a4W7E4oJXDJLf330bqdeoxNXJZ/ToTxbpUzudsIUhCj8zhpPEugh8H
+cMBOYzvY0oqZH4tFK68Moc6Swp3r54y3Lqg/ON2QaarIQReZA0/dCr0wUkp5Kla1
+fKecUPf8iPopaOKqFmzeDHuh10ZYSVQM1LW3FCHDAoGBALnznkWyJTWD91EbrHTY
+9f+9gqk/YgNzxlLuxgiQQSSVwkKxp6NOixFITkApnxs0BIf8sF5dxSpfq2Lb8W0k
+5tno3RoDvLc6XISOm4vtz0UwLhP9vREG3YY650nb/zhLBU8RhAlqOYCp+DqekNr7
+63FsxMyA82eHX33/t5ewrPWZAoGBAKuC762wkH8zZYHyQkBCbFsHo2xNaD5unt7k
+rI+Z1A0KAcxDYRrbB2+TtQUDaBr2qkM9Crj7kLQKzqvHHgnt4tj2VZ6jNhr2N3TI
+5/bs8oXRTfzG+5vhuMphACEhpQ6ZppK27N8uuLQL3V0Lcu59hWYfU8+bbc50DGn7
++yFYa7gfAoGBANatpQuu9CwP3/tKkDN8jzDo7n824celL8ANt6CflU0HGW3IKtRN
+8E4UVfpuSjU1+KQ4a07wqXMHmY3lIdZa4UOut8Ug/os8+NydlhxuQufFjdtjPYP2
+Mq7zrVhebbk+X+vuZdsV6VUr+Xx4n6BpxZ7nf3q0UtrVmHbDVg7QfKjR
+-----END RSA PRIVATE KEY-----
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html
index 74e97a032a..5454747936 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html
@@ -2,7 +2,7 @@
-
\ No newline at end of file
+