KEYCLOAK-9846 Verifying signatures on CRL during X509 authentication
This commit is contained in:
parent
2899375614
commit
5f9feee3f8
22 changed files with 562 additions and 217 deletions
|
@ -1,92 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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.common.util;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.bouncycastle.asn1.ASN1InputStream;
|
|
||||||
import org.bouncycastle.asn1.DERIA5String;
|
|
||||||
import org.bouncycastle.asn1.DEROctetString;
|
|
||||||
import org.bouncycastle.asn1.x509.CRLDistPoint;
|
|
||||||
import org.bouncycastle.asn1.x509.DistributionPoint;
|
|
||||||
import org.bouncycastle.asn1.x509.DistributionPointName;
|
|
||||||
import org.bouncycastle.asn1.x509.GeneralName;
|
|
||||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:brat000012001@gmail.com">Peter Nalyvayko</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
* @since 10/31/2016
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class CRLUtils {
|
|
||||||
|
|
||||||
static {
|
|
||||||
BouncyIntegration.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String CRL_DISTRIBUTION_POINTS_OID = "2.5.29.31";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a list of CRL distribution points from CRLDP v3 certificate extension
|
|
||||||
* See <a href="www.nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-cchain-and-verify-clr-with-bouncy-castle/">CRL validation</a>
|
|
||||||
* @param cert
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static List<String> getCRLDistributionPoints(X509Certificate cert) throws IOException {
|
|
||||||
byte[] data = cert.getExtensionValue(CRL_DISTRIBUTION_POINTS_OID);
|
|
||||||
if (data == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> distributionPointUrls = new LinkedList<>();
|
|
||||||
DEROctetString octetString;
|
|
||||||
try (ASN1InputStream crldpExtensionInputStream = new ASN1InputStream(new ByteArrayInputStream(data))) {
|
|
||||||
octetString = (DEROctetString)crldpExtensionInputStream.readObject();
|
|
||||||
}
|
|
||||||
byte[] octets = octetString.getOctets();
|
|
||||||
|
|
||||||
CRLDistPoint crlDP;
|
|
||||||
try (ASN1InputStream crldpInputStream = new ASN1InputStream(new ByteArrayInputStream(octets))) {
|
|
||||||
crlDP = CRLDistPoint.getInstance(crldpInputStream.readObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DistributionPoint dp : crlDP.getDistributionPoints()) {
|
|
||||||
DistributionPointName dpn = dp.getDistributionPoint();
|
|
||||||
if (dpn != null && dpn.getType() == DistributionPointName.FULL_NAME) {
|
|
||||||
GeneralName[] names = GeneralNames.getInstance(dpn.getName()).getNames();
|
|
||||||
for (GeneralName gn : names) {
|
|
||||||
if (gn.getTagNo() == GeneralName.uniformResourceIdentifier) {
|
|
||||||
String url = DERIA5String.getInstance(gn.getName()).getString();
|
|
||||||
distributionPointUrls.add(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return distributionPointUrls;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -20,6 +20,10 @@ package org.keycloak.truststore;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -29,4 +33,14 @@ public interface TruststoreProvider extends Provider {
|
||||||
HostnameVerificationPolicy getPolicy();
|
HostnameVerificationPolicy getPolicy();
|
||||||
|
|
||||||
KeyStore getTruststore();
|
KeyStore getTruststore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return root certificates from the configured truststore as a map where the key is the X500Principal of the corresponding X509Certificate
|
||||||
|
*/
|
||||||
|
Map<X500Principal, X509Certificate> getRootCertificates();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return intermediate certificates from the configured truststore as a map where the key is the X500Principal of the corresponding X509Certificate
|
||||||
|
*/
|
||||||
|
Map<X500Principal, X509Certificate> getIntermediateCertificates();
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,10 +86,11 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth
|
||||||
|
|
||||||
protected static class CertificateValidatorConfigBuilder {
|
protected static class CertificateValidatorConfigBuilder {
|
||||||
|
|
||||||
static CertificateValidator.CertificateValidatorBuilder fromConfig(X509AuthenticatorConfigModel config) throws Exception {
|
static CertificateValidator.CertificateValidatorBuilder fromConfig(KeycloakSession session, X509AuthenticatorConfigModel config) throws Exception {
|
||||||
|
|
||||||
CertificateValidator.CertificateValidatorBuilder builder = new CertificateValidator.CertificateValidatorBuilder();
|
CertificateValidator.CertificateValidatorBuilder builder = new CertificateValidator.CertificateValidatorBuilder();
|
||||||
return builder
|
return builder
|
||||||
|
.session(session)
|
||||||
.keyUsage()
|
.keyUsage()
|
||||||
.parse(config.getKeyUsage())
|
.parse(config.getKeyUsage())
|
||||||
.extendedKeyUsage()
|
.extendedKeyUsage()
|
||||||
|
@ -105,8 +106,8 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
// The method is purely for purposes of facilitating the unit testing
|
// The method is purely for purposes of facilitating the unit testing
|
||||||
public CertificateValidator.CertificateValidatorBuilder certificateValidationParameters(X509AuthenticatorConfigModel config) throws Exception {
|
public CertificateValidator.CertificateValidatorBuilder certificateValidationParameters(KeycloakSession session, X509AuthenticatorConfigModel config) throws Exception {
|
||||||
return CertificateValidatorConfigBuilder.fromConfig(config);
|
return CertificateValidatorConfigBuilder.fromConfig(session, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class UserIdentityExtractorBuilder {
|
protected static class UserIdentityExtractorBuilder {
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
|
|
||||||
package org.keycloak.authentication.authenticators.x509;
|
package org.keycloak.authentication.authenticators.x509;
|
||||||
|
|
||||||
import org.keycloak.common.util.CRLUtils;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.utils.CRLUtils;
|
||||||
import org.keycloak.common.util.OCSPUtils;
|
import org.keycloak.common.util.OCSPUtils;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
|
@ -54,7 +55,6 @@ import java.util.Set;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
||||||
|
@ -354,7 +354,7 @@ public class CertificateValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeycloakSession session;
|
||||||
X509Certificate[] _certChain;
|
X509Certificate[] _certChain;
|
||||||
int _keyUsageBits;
|
int _keyUsageBits;
|
||||||
List<String> _extendedKeyUsage;
|
List<String> _extendedKeyUsage;
|
||||||
|
@ -373,7 +373,8 @@ public class CertificateValidator {
|
||||||
boolean cRLDPCheckingEnabled,
|
boolean cRLDPCheckingEnabled,
|
||||||
CRLLoaderImpl crlLoader,
|
CRLLoaderImpl crlLoader,
|
||||||
boolean oCSPCheckingEnabled,
|
boolean oCSPCheckingEnabled,
|
||||||
OCSPChecker ocspChecker) {
|
OCSPChecker ocspChecker,
|
||||||
|
KeycloakSession session) {
|
||||||
_certChain = certChain;
|
_certChain = certChain;
|
||||||
_keyUsageBits = keyUsageBits;
|
_keyUsageBits = keyUsageBits;
|
||||||
_extendedKeyUsage = extendedKeyUsage;
|
_extendedKeyUsage = extendedKeyUsage;
|
||||||
|
@ -382,6 +383,7 @@ public class CertificateValidator {
|
||||||
_crlLoader = crlLoader;
|
_crlLoader = crlLoader;
|
||||||
_ocspEnabled = oCSPCheckingEnabled;
|
_ocspEnabled = oCSPCheckingEnabled;
|
||||||
this.ocspChecker = ocspChecker;
|
this.ocspChecker = ocspChecker;
|
||||||
|
this.session = session;
|
||||||
|
|
||||||
if (ocspChecker == null)
|
if (ocspChecker == null)
|
||||||
throw new IllegalArgumentException("ocspChecker");
|
throw new IllegalArgumentException("ocspChecker");
|
||||||
|
@ -497,15 +499,11 @@ public class CertificateValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkRevocationStatusUsingCRL(X509Certificate[] certs, CRLLoaderImpl crLoader) throws GeneralSecurityException {
|
private static void checkRevocationStatusUsingCRL(X509Certificate[] certs, CRLLoaderImpl crLoader, KeycloakSession session) throws GeneralSecurityException {
|
||||||
Collection<X509CRL> crlColl = crLoader.getX509CRLs();
|
Collection<X509CRL> crlColl = crLoader.getX509CRLs();
|
||||||
if (crlColl != null && crlColl.size() > 0) {
|
if (crlColl != null && crlColl.size() > 0) {
|
||||||
for (X509CRL it : crlColl) {
|
for (X509CRL it : crlColl) {
|
||||||
if (it.isRevoked(certs[0])) {
|
CRLUtils.check(certs, it, session);
|
||||||
String message = String.format("Certificate has been revoked, certificate's subject: %s", certs[0].getSubjectDN().getName());
|
|
||||||
logger.debug(message);
|
|
||||||
throw new GeneralSecurityException(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -519,7 +517,7 @@ public class CertificateValidator {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkRevocationStatusUsingCRLDistributionPoints(X509Certificate[] certs) throws GeneralSecurityException {
|
private static void checkRevocationStatusUsingCRLDistributionPoints(X509Certificate[] certs, KeycloakSession session) throws GeneralSecurityException {
|
||||||
|
|
||||||
List<String> distributionPoints = getCRLDistributionPoints(certs[0]);
|
List<String> distributionPoints = getCRLDistributionPoints(certs[0]);
|
||||||
if (distributionPoints == null || distributionPoints.size() == 0) {
|
if (distributionPoints == null || distributionPoints.size() == 0) {
|
||||||
|
@ -527,7 +525,7 @@ public class CertificateValidator {
|
||||||
}
|
}
|
||||||
for (String dp : distributionPoints) {
|
for (String dp : distributionPoints) {
|
||||||
logger.tracef("CRL Distribution point: \"%s\"", dp);
|
logger.tracef("CRL Distribution point: \"%s\"", dp);
|
||||||
checkRevocationStatusUsingCRL(certs, new CRLFileLoader(dp));
|
checkRevocationStatusUsingCRL(certs, new CRLFileLoader(dp), session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,9 +535,9 @@ public class CertificateValidator {
|
||||||
}
|
}
|
||||||
if (_crlCheckingEnabled) {
|
if (_crlCheckingEnabled) {
|
||||||
if (!_crldpEnabled) {
|
if (!_crldpEnabled) {
|
||||||
checkRevocationStatusUsingCRL(_certChain, _crlLoader /*"crl.pem"*/);
|
checkRevocationStatusUsingCRL(_certChain, _crlLoader, session);
|
||||||
} else {
|
} else {
|
||||||
checkRevocationStatusUsingCRLDistributionPoints(_certChain);
|
checkRevocationStatusUsingCRLDistributionPoints(_certChain, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_ocspEnabled) {
|
if (_ocspEnabled) {
|
||||||
|
@ -556,6 +554,7 @@ public class CertificateValidator {
|
||||||
// instances of CertificateValidator type. The design is an adaption of
|
// instances of CertificateValidator type. The design is an adaption of
|
||||||
// the approach described in http://programmers.stackexchange.com/questions/252067/learning-to-write-dsls-utilities-for-unit-tests-and-am-worried-about-extensablit
|
// the approach described in http://programmers.stackexchange.com/questions/252067/learning-to-write-dsls-utilities-for-unit-tests-and-am-worried-about-extensablit
|
||||||
|
|
||||||
|
KeycloakSession session;
|
||||||
int _keyUsageBits;
|
int _keyUsageBits;
|
||||||
List<String> _extendedKeyUsage;
|
List<String> _extendedKeyUsage;
|
||||||
boolean _crlCheckingEnabled;
|
boolean _crlCheckingEnabled;
|
||||||
|
@ -732,6 +731,11 @@ public class CertificateValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CertificateValidatorBuilder session(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public KeyUsageValidationBuilder keyUsage() {
|
public KeyUsageValidationBuilder keyUsage() {
|
||||||
return new KeyUsageValidationBuilder(this);
|
return new KeyUsageValidationBuilder(this);
|
||||||
}
|
}
|
||||||
|
@ -750,7 +754,7 @@ public class CertificateValidator {
|
||||||
}
|
}
|
||||||
return new CertificateValidator(certs, _keyUsageBits, _extendedKeyUsage,
|
return new CertificateValidator(certs, _keyUsageBits, _extendedKeyUsage,
|
||||||
_crlCheckingEnabled, _crldpEnabled, _crlLoader, _ocspEnabled,
|
_crlCheckingEnabled, _crldpEnabled, _crlLoader, _ocspEnabled,
|
||||||
new BouncyCastleOCSPChecker(_responderUri, _responderCert));
|
new BouncyCastleOCSPChecker(_responderUri, _responderCert), session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class ValidateX509CertificateUsername extends AbstractX509ClientCertifica
|
||||||
}
|
}
|
||||||
// Validate X509 client certificate
|
// Validate X509 client certificate
|
||||||
try {
|
try {
|
||||||
CertificateValidator.CertificateValidatorBuilder builder = certificateValidationParameters(config);
|
CertificateValidator.CertificateValidatorBuilder builder = certificateValidationParameters(context.getSession(), config);
|
||||||
CertificateValidator validator = builder.build(certs);
|
CertificateValidator validator = builder.build(certs);
|
||||||
validator.checkRevocationStatus()
|
validator.checkRevocationStatus()
|
||||||
.validateKeyUsage()
|
.validateKeyUsage()
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class X509ClientCertificateAuthenticator extends AbstractX509ClientCertif
|
||||||
|
|
||||||
// Validate X509 client certificate
|
// Validate X509 client certificate
|
||||||
try {
|
try {
|
||||||
CertificateValidator.CertificateValidatorBuilder builder = certificateValidationParameters(config);
|
CertificateValidator.CertificateValidatorBuilder builder = certificateValidationParameters(context.getSession(), config);
|
||||||
CertificateValidator validator = builder.build(certs);
|
CertificateValidator validator = builder.build(certs);
|
||||||
validator.checkRevocationStatus()
|
validator.checkRevocationStatus()
|
||||||
.validateKeyUsage()
|
.validateKeyUsage()
|
||||||
|
|
|
@ -3,26 +3,20 @@ package org.keycloak.services.x509;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.cert.CertPath;
|
import java.security.cert.CertPath;
|
||||||
import java.security.cert.CertPathBuilder;
|
import java.security.cert.CertPathBuilder;
|
||||||
import java.security.cert.CertPathBuilderException;
|
import java.security.cert.CertPathBuilderException;
|
||||||
import java.security.cert.CertStore;
|
import java.security.cert.CertStore;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.CollectionCertStoreParameters;
|
import java.security.cert.CollectionCertStoreParameters;
|
||||||
import java.security.cert.PKIXBuilderParameters;
|
import java.security.cert.PKIXBuilderParameters;
|
||||||
import java.security.cert.TrustAnchor;
|
import java.security.cert.TrustAnchor;
|
||||||
import java.security.cert.X509CertSelector;
|
import java.security.cert.X509CertSelector;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -253,7 +247,8 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
|
||||||
|
|
||||||
if ( provider != null && provider.getTruststore() != null ) {
|
if ( provider != null && provider.getTruststore() != null ) {
|
||||||
truststore = provider.getTruststore();
|
truststore = provider.getTruststore();
|
||||||
readTruststore();
|
trustedRootCerts = new HashSet<>(provider.getRootCertificates().values());
|
||||||
|
intermediateCerts = new HashSet<>(provider.getIntermediateCertificates().values());
|
||||||
log.debug("Keycloak truststore loaded for NGINX x509cert-lookup provider.");
|
log.debug("Keycloak truststore loaded for NGINX x509cert-lookup provider.");
|
||||||
|
|
||||||
isTruststoreLoaded = true;
|
isTruststoreLoaded = true;
|
||||||
|
@ -263,70 +258,4 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
|
||||||
return isTruststoreLoaded;
|
return isTruststoreLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all certificates from Keycloak Truststore, and classify them in two lists : root CAs and intermediates CAs
|
|
||||||
*/
|
|
||||||
private void readTruststore() {
|
|
||||||
|
|
||||||
//Reading truststore aliases & certificates
|
|
||||||
Enumeration enumeration;
|
|
||||||
|
|
||||||
trustedRootCerts = new HashSet<X509Certificate>();
|
|
||||||
intermediateCerts = new HashSet<X509Certificate>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
enumeration = truststore.aliases();
|
|
||||||
log.trace("Checking " + truststore.size() + " entries from the truststore.");
|
|
||||||
while(enumeration.hasMoreElements()) {
|
|
||||||
|
|
||||||
String alias = (String)enumeration.nextElement();
|
|
||||||
Certificate certificate = truststore.getCertificate(alias);
|
|
||||||
|
|
||||||
if (certificate instanceof X509Certificate) {
|
|
||||||
X509Certificate cax509cert = (X509Certificate) certificate;
|
|
||||||
if (isSelfSigned(cax509cert)) {
|
|
||||||
trustedRootCerts.add(cax509cert);
|
|
||||||
log.debug("Trusted root CA found in trustore : alias : "+alias + " | Subject DN : " + ((X509Certificate) certificate).getSubjectDN() );
|
|
||||||
} else {
|
|
||||||
intermediateCerts.add(cax509cert);
|
|
||||||
log.debug("Intermediate CA found in trustore : alias : "+alias + " | Subject DN : " + ((X509Certificate) certificate).getSubjectDN() );
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
log.info("Skipping certificate with alias ["+ alias + "] from truststore, because it's not an X509Certificate");
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (KeyStoreException e) {
|
|
||||||
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
|
||||||
} catch (NoSuchProviderException e) {
|
|
||||||
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether given X.509 certificate is self-signed.
|
|
||||||
*/
|
|
||||||
public boolean isSelfSigned(X509Certificate cert)
|
|
||||||
throws CertificateException, NoSuchAlgorithmException,
|
|
||||||
NoSuchProviderException {
|
|
||||||
try {
|
|
||||||
// Try to verify certificate signature with its own public key
|
|
||||||
PublicKey key = cert.getPublicKey();
|
|
||||||
cert.verify(key);
|
|
||||||
log.trace("certificate " + cert.getSubjectDN() + " detected as root CA");
|
|
||||||
return true;
|
|
||||||
} catch (SignatureException sigEx) {
|
|
||||||
// Invalid signature --> not self-signed
|
|
||||||
log.trace("certificate " + cert.getSubjectDN() + " detected as intermediate CA");
|
|
||||||
} catch (InvalidKeyException keyEx) {
|
|
||||||
// Invalid key --> not self-signed
|
|
||||||
log.trace("certificate " + cert.getSubjectDN() + " detected as intermediate CA");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
package org.keycloak.truststore;
|
package org.keycloak.truststore;
|
||||||
|
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -26,10 +30,14 @@ public class FileTruststoreProvider implements TruststoreProvider {
|
||||||
|
|
||||||
private final HostnameVerificationPolicy policy;
|
private final HostnameVerificationPolicy policy;
|
||||||
private final KeyStore truststore;
|
private final KeyStore truststore;
|
||||||
|
private final Map<X500Principal, X509Certificate> rootCertificates;
|
||||||
|
private final Map<X500Principal, X509Certificate> intermediateCertificates;
|
||||||
|
|
||||||
FileTruststoreProvider(KeyStore truststore, HostnameVerificationPolicy policy) {
|
FileTruststoreProvider(KeyStore truststore, HostnameVerificationPolicy policy, Map<X500Principal, X509Certificate> rootCertificates, Map<X500Principal, X509Certificate> intermediateCertificates) {
|
||||||
this.policy = policy;
|
this.policy = policy;
|
||||||
this.truststore = truststore;
|
this.truststore = truststore;
|
||||||
|
this.rootCertificates = rootCertificates;
|
||||||
|
this.intermediateCertificates = intermediateCertificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,6 +50,16 @@ public class FileTruststoreProvider implements TruststoreProvider {
|
||||||
return truststore;
|
return truststore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<X500Principal, X509Certificate> getRootCertificates() {
|
||||||
|
return rootCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<X500Principal, X509Certificate> getIntermediateCertificates() {
|
||||||
|
return intermediateCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,22 @@ import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -85,7 +100,8 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provider = new FileTruststoreProvider(truststore, verificationPolicy);
|
TruststoreCertificatesLoader certsLoader = new TruststoreCertificatesLoader(truststore);
|
||||||
|
provider = new FileTruststoreProvider(truststore, verificationPolicy, certsLoader.trustedRootCerts, certsLoader.intermediateCerts);
|
||||||
TruststoreProviderSingleton.set(provider);
|
TruststoreProviderSingleton.set(provider);
|
||||||
log.debug("File trustore provider initialized: " + new File(storepath).getAbsolutePath());
|
log.debug("File trustore provider initialized: " + new File(storepath).getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
@ -116,4 +132,82 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "file";
|
return "file";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class TruststoreCertificatesLoader {
|
||||||
|
|
||||||
|
private Map<X500Principal, X509Certificate> trustedRootCerts = new HashMap<>();
|
||||||
|
private Map<X500Principal, X509Certificate> intermediateCerts = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public TruststoreCertificatesLoader(KeyStore truststore) {
|
||||||
|
readTruststore(truststore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all certificates from Keycloak Truststore, and classify them in two lists : root CAs and intermediates CAs
|
||||||
|
*/
|
||||||
|
private void readTruststore(KeyStore truststore) {
|
||||||
|
|
||||||
|
//Reading truststore aliases & certificates
|
||||||
|
Enumeration enumeration;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
enumeration = truststore.aliases();
|
||||||
|
log.trace("Checking " + truststore.size() + " entries from the truststore.");
|
||||||
|
while(enumeration.hasMoreElements()) {
|
||||||
|
|
||||||
|
String alias = (String)enumeration.nextElement();
|
||||||
|
Certificate certificate = truststore.getCertificate(alias);
|
||||||
|
|
||||||
|
if (certificate instanceof X509Certificate) {
|
||||||
|
X509Certificate cax509cert = (X509Certificate) certificate;
|
||||||
|
if (isSelfSigned(cax509cert)) {
|
||||||
|
X500Principal principal = cax509cert.getSubjectX500Principal();
|
||||||
|
trustedRootCerts.put(principal, cax509cert);
|
||||||
|
log.debug("Trusted root CA found in trustore : alias : "+alias + " | Subject DN : " + principal);
|
||||||
|
} else {
|
||||||
|
X500Principal principal = cax509cert.getSubjectX500Principal();
|
||||||
|
intermediateCerts.put(principal, cax509cert);
|
||||||
|
log.debug("Intermediate CA found in trustore : alias : "+alias + " | Subject DN : " + principal);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
log.info("Skipping certificate with alias ["+ alias + "] from truststore, because it's not an X509Certificate");
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
||||||
|
} catch (NoSuchProviderException e) {
|
||||||
|
log.error("Error while reading Keycloak truststore "+e.getMessage(),e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether given X.509 certificate is self-signed.
|
||||||
|
*/
|
||||||
|
private boolean isSelfSigned(X509Certificate cert)
|
||||||
|
throws CertificateException, NoSuchAlgorithmException,
|
||||||
|
NoSuchProviderException {
|
||||||
|
try {
|
||||||
|
// Try to verify certificate signature with its own public key
|
||||||
|
PublicKey key = cert.getPublicKey();
|
||||||
|
cert.verify(key);
|
||||||
|
log.trace("certificate " + cert.getSubjectDN() + " detected as root CA");
|
||||||
|
return true;
|
||||||
|
} catch (SignatureException sigEx) {
|
||||||
|
// Invalid signature --> not self-signed
|
||||||
|
log.trace("certificate " + cert.getSubjectDN() + " detected as intermediate CA");
|
||||||
|
} catch (InvalidKeyException keyEx) {
|
||||||
|
// Invalid key --> not self-signed
|
||||||
|
log.trace("certificate " + cert.getSubjectDN() + " detected as intermediate CA");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
206
services/src/main/java/org/keycloak/utils/CRLUtils.java
Normal file
206
services/src/main/java/org/keycloak/utils/CRLUtils.java
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Red Hat, 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.utils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.cert.X509CRL;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.ASN1InputStream;
|
||||||
|
import org.bouncycastle.asn1.DERIA5String;
|
||||||
|
import org.bouncycastle.asn1.DEROctetString;
|
||||||
|
import org.bouncycastle.asn1.x509.CRLDistPoint;
|
||||||
|
import org.bouncycastle.asn1.x509.DistributionPoint;
|
||||||
|
import org.bouncycastle.asn1.x509.DistributionPointName;
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralName;
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.common.util.BouncyIntegration;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.truststore.TruststoreProvider;
|
||||||
|
import org.wildfly.security.x500.X500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:brat000012001@gmail.com">Peter Nalyvayko</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
* @since 10/31/2016
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class CRLUtils {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(CRLUtils.class);
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
BouncyIntegration.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String CRL_DISTRIBUTION_POINTS_OID = "2.5.29.31";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of CRL distribution points from CRLDP v3 certificate extension
|
||||||
|
* See <a href="www.nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-cchain-and-verify-clr-with-bouncy-castle/">CRL validation</a>
|
||||||
|
* @param cert
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static List<String> getCRLDistributionPoints(X509Certificate cert) throws IOException {
|
||||||
|
byte[] data = cert.getExtensionValue(CRL_DISTRIBUTION_POINTS_OID);
|
||||||
|
if (data == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> distributionPointUrls = new LinkedList<>();
|
||||||
|
DEROctetString octetString;
|
||||||
|
try (ASN1InputStream crldpExtensionInputStream = new ASN1InputStream(new ByteArrayInputStream(data))) {
|
||||||
|
octetString = (DEROctetString)crldpExtensionInputStream.readObject();
|
||||||
|
}
|
||||||
|
byte[] octets = octetString.getOctets();
|
||||||
|
|
||||||
|
CRLDistPoint crlDP;
|
||||||
|
try (ASN1InputStream crldpInputStream = new ASN1InputStream(new ByteArrayInputStream(octets))) {
|
||||||
|
crlDP = CRLDistPoint.getInstance(crldpInputStream.readObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DistributionPoint dp : crlDP.getDistributionPoints()) {
|
||||||
|
DistributionPointName dpn = dp.getDistributionPoint();
|
||||||
|
if (dpn != null && dpn.getType() == DistributionPointName.FULL_NAME) {
|
||||||
|
GeneralName[] names = GeneralNames.getInstance(dpn.getName()).getNames();
|
||||||
|
for (GeneralName gn : names) {
|
||||||
|
if (gn.getTagNo() == GeneralName.uniformResourceIdentifier) {
|
||||||
|
String url = DERIA5String.getInstance(gn.getName()).getString();
|
||||||
|
distributionPointUrls.add(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distributionPointUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the signature on CRL and check if 1st certificate from the chain ((The actual certificate from the client)) is valid and not available on CRL.
|
||||||
|
*
|
||||||
|
* @param certs The 1st certificate is the actual certificate of the user. The other certificates represents the certificate chain
|
||||||
|
* @param crl Given CRL
|
||||||
|
* @throws GeneralSecurityException if some error in validation happens. Typically certificate not valid, or CRL signature not valid
|
||||||
|
*/
|
||||||
|
public static void check(X509Certificate[] certs, X509CRL crl, KeycloakSession session) throws GeneralSecurityException {
|
||||||
|
if (certs.length < 2) {
|
||||||
|
throw new GeneralSecurityException("Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it");
|
||||||
|
}
|
||||||
|
|
||||||
|
X500Principal crlIssuerPrincipal = crl.getIssuerX500Principal();
|
||||||
|
X509Certificate crlSignatureCertificate = null;
|
||||||
|
|
||||||
|
// Try to find the certificate in the CA chain, which was used to sign the CRL
|
||||||
|
for (int i=1 ; i<certs.length ; i++) {
|
||||||
|
X509Certificate currentCACert = certs[i];
|
||||||
|
if (crlIssuerPrincipal.equals(currentCACert.getSubjectX500Principal())) {
|
||||||
|
crlSignatureCertificate = currentCACert;
|
||||||
|
|
||||||
|
log.tracef("Found certificate used to sign CRL in the CA chain of the certificate. CRL issuer: %s", crlIssuerPrincipal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the CRL issuer certificate in the truststore
|
||||||
|
if (crlSignatureCertificate == null) {
|
||||||
|
log.tracef("Not found CRL issuer '%s' in the CA chain of the certificate. Fallback to lookup CRL issuer in the truststore", crlIssuerPrincipal);
|
||||||
|
crlSignatureCertificate = findCRLSignatureCertificateInTruststore(session, certs, crlIssuerPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signature on CRL
|
||||||
|
// TODO: It will be nice to cache CRLs and also verify their signatures just once at the time when CRL is loaded, rather than in every request
|
||||||
|
crl.verify(crlSignatureCertificate.getPublicKey());
|
||||||
|
|
||||||
|
// Finally check if
|
||||||
|
if (crl.isRevoked(certs[0])) {
|
||||||
|
String message = String.format("Certificate has been revoked, certificate's subject: %s", certs[0].getSubjectDN().getName());
|
||||||
|
log.debug(message);
|
||||||
|
throw new GeneralSecurityException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static X509Certificate findCRLSignatureCertificateInTruststore(KeycloakSession session, X509Certificate[] certs, X500Principal crlIssuerPrincipal) throws GeneralSecurityException {
|
||||||
|
TruststoreProvider truststoreProvider = session.getProvider(TruststoreProvider.class);
|
||||||
|
if (truststoreProvider == null || truststoreProvider.getTruststore() == null) {
|
||||||
|
throw new GeneralSecurityException("Truststore not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<X500Principal, X509Certificate> rootCerts = truststoreProvider.getRootCertificates();
|
||||||
|
Map<X500Principal, X509Certificate> intermediateCerts = truststoreProvider.getIntermediateCertificates();
|
||||||
|
|
||||||
|
X509Certificate crlSignatureCertificate = intermediateCerts.get(crlIssuerPrincipal);
|
||||||
|
if (crlSignatureCertificate == null) {
|
||||||
|
crlSignatureCertificate = rootCerts.get(crlIssuerPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crlSignatureCertificate == null) {
|
||||||
|
throw new GeneralSecurityException("Not available certificate for CRL issuer '" + crlIssuerPrincipal + "' in the truststore, nor in the CA chain");
|
||||||
|
} else {
|
||||||
|
log.tracef("Found CRL issuer certificate with subject '%s' in the truststore. Verifying trust anchor", crlIssuerPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if CRL issuer has trust anchor with the checked certificate (See https://tools.ietf.org/html/rfc5280#section-6.3.3 , paragraph (f))
|
||||||
|
Set<X500Principal> certificateCAPrincipals = Arrays.asList(certs).stream()
|
||||||
|
.map(X509Certificate::getSubjectX500Principal)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// Remove the checked certificate itself
|
||||||
|
certificateCAPrincipals.remove(certs[0].getSubjectX500Principal());
|
||||||
|
|
||||||
|
X509Certificate currentCRLAnchorCertificate = crlSignatureCertificate;
|
||||||
|
X500Principal currentCRLAnchorPrincipal = crlIssuerPrincipal;
|
||||||
|
while (true) {
|
||||||
|
if (certificateCAPrincipals.contains(currentCRLAnchorPrincipal)) {
|
||||||
|
log.tracef("Found trust anchor of the CRL issuer '%s' in the CA chain. Anchor is '%s'", crlIssuerPrincipal, currentCRLAnchorPrincipal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to see the anchor
|
||||||
|
currentCRLAnchorPrincipal = currentCRLAnchorCertificate.getIssuerX500Principal();
|
||||||
|
|
||||||
|
currentCRLAnchorCertificate = intermediateCerts.get(currentCRLAnchorPrincipal);
|
||||||
|
if (currentCRLAnchorCertificate == null) {
|
||||||
|
currentCRLAnchorCertificate = rootCerts.get(currentCRLAnchorPrincipal);
|
||||||
|
}
|
||||||
|
if (currentCRLAnchorCertificate == null) {
|
||||||
|
throw new GeneralSecurityException("Certificate for CRL issuer '" + crlIssuerPrincipal + "' available in the truststore, but doesn't have trust anchors with the CA chain.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crlSignatureCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,26 +1,69 @@
|
||||||
Bag Attributes
|
|
||||||
friendlyName: localhost
|
|
||||||
localKeyID: 54 69 6D 65 20 31 34 37 37 32 37 36 33 32 32 32 32 35
|
|
||||||
subject=/C=US/ST=MA/L=Westword/O=Red Hat/OU=Keycloak/CN=localhost
|
|
||||||
issuer=/C=US/ST=MA/L=Westword/O=Red Hat/OU=Keycloak/CN=localhost
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
||||||
M9ES3g9Wj9OcCsHzrTAm
|
AQpKczbt2goTyb2Annhpa5WJe/sYvYqTUwIDAQABo2YwZDAdBgNVHQ4EFgQURxJ8
|
||||||
|
iQtHVxlUCvUDBM2fhqKWdpQwHwYDVR0jBBgwFoAUIrj0u3MAxyk/k4Cl9hxSAmrL
|
||||||
|
elIwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN
|
||||||
|
AQELBQADggIBAFwmiG2sd77dmX+klIeLVIYq4X3VwNijwzpuilDPMqSfSlBawj8f
|
||||||
|
PjwFJYzpcl2pe/Lq6sq96VMkN65/AUs/XZOW+ybgE7ZuJlfT12sk48TPgaVvP2dJ
|
||||||
|
5ud2l+DWYaH6KjU3B/xx8xttN73BilMobaJMDy02TLK6VgHPtV3bRyPOQNsGrOmp
|
||||||
|
wJMPi7t9UjcMm0THhVHdP881ryGXraNb38x5AgTILUwRYmwjtc1Rrlls0eKLtoAl
|
||||||
|
n5oScPDPeZELVunFFJ/ZX2lx5yApWpP1sMyzvJxnZhruuzfxsW60Tp+6Q8rHkabw
|
||||||
|
ZnnkHgi53/Gnp3H7l/kszM+hNYJXTDTHdPTQMETHEHqiWOzYttBTM8p/ffb3haTm
|
||||||
|
UnPb5fuRXJxX8vMxA1h6nSFWtQEQbvlGiS2oGNAOi5XlTsE+mjYMALuAPID9v8Yx
|
||||||
|
3eTyI7a4I+qy3a+0Q1iBFsAM75q6cbne7LK8FjLHDnZvHOnredoR/tmebgphD4C3
|
||||||
|
p4xNlwocSs+Fhjqsf6L5AvAc8fLP1206f/lp/9qEnvD0kocw2KvxwZY2yDtf115z
|
||||||
|
aHxhil32iWME340LVSYyQZqwPPr3N2t4CGZsgGs8vPXLECAGqrT3V2/I3iZNF3J5
|
||||||
|
i0GE63/1Q35BPHxPAJcqB/a5woBwo/Ae40u6qWR15keFp3UaJ0M/C9GR
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
SCc=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIB3zCByAIBATANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||||
|
CAwCTUExEDAOBgNVBAoMB1JlZCBIYXQxETAPBgNVBAsMCEtleWNsb2FrMSMwIQYD
|
||||||
|
VQQDDBpLZXljbG9hayBJbnRlcm1lZGlhdGUgQ0EgMxcNMTkwMzIxMTgzNTQ3WhcN
|
||||||
|
MTkwNDIwMTgzNTQ3WqAwMC4wHwYDVR0jBBgwFoAUGFyPCYy3yxTPqAoIcfLf0fto
|
||||||
|
r9MwCwYDVR0UBAQCAhACMA0GCSqGSIb3DQEBCwUAA4IBAQDlowilp+jZ2OtWpwSa
|
||||||
|
bE2Cglm/6K8pNAzPROSv+yEc0aFUT+tm6yRWaITy+1hl5cLh2QAJk3E7gtXRhhXc
|
||||||
|
mf39U0k/WOSmcZCCUwgJuhudpeweFk7XOL3+Jr0npRTaWP583nZNVZQzFxHBZ5T/
|
||||||
|
7H3rzjcuvYo9IzRroTNEaol19EgavrnAmG2jMM4Q/9YdYIBjxFJAigH/uGV51O0G
|
||||||
|
EV4PNDm+uCvfYQfa1GsGCszaUH2Ixpl9Jr2MP6LN5/w4dGzdq+yDz1srsfgFzVNj
|
||||||
|
cBz3lf1ogFKeRmLF2tTNJ1HLTsOImjvnZqRiD/3EkzLBMsptVFns+/Aq5MRo1pKJ
|
||||||
|
iNEi
|
||||||
|
-----END X509 CRL-----
|
|
@ -0,0 +1,17 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIICsjCCAZoCAQEwDQYJKoZIhvcNAQELBQAwgYcxCzAJBgNVBAYTAlVTMQswCQYD
|
||||||
|
VQQIDAJNQTEQMA4GA1UECgwHUmVkIEhhdDERMA8GA1UECwwIS2V5Y2xvYWsxITAf
|
||||||
|
BgNVBAMMGEtleWNsb2FrIEludGVybWVkaWF0ZSBDQTEjMCEGCSqGSIb3DQEJARYU
|
||||||
|
Y29udGFjdEBrZXljbG9hay5vcmcXDTE5MDMyMDE5Mjk1MVoXDTE5MDQxOTE5Mjk1
|
||||||
|
MVowFTATAgIQCRcNMTkwMzIwMTkyOTUxWqCBxjCBwzCBtAYDVR0jBIGsMIGpgBRw
|
||||||
|
AVgUGev7EtO+WNLtEcYGQYoadaGBjaSBijCBhzELMAkGA1UEBhMCVVMxCzAJBgNV
|
||||||
|
BAgMAk1BMRAwDgYDVQQKDAdSZWQgSGF0MREwDwYDVQQLDAhLZXljbG9hazEhMB8G
|
||||||
|
A1UEAwwYS2V5Y2xvYWsgSW50ZXJtZWRpYXRlIENBMSMwIQYJKoZIhvcNAQkBFhRj
|
||||||
|
b250YWN0QGtleWNsb2FrLm9yZ4IBATAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsF
|
||||||
|
AAOCAQEAmurquuW//sYdX0WrAeBDXY81Y0riz9mYgmrYKXMANE2fpybzReYnI0WC
|
||||||
|
4JRzfKfdLYWbcJCxdKpsRyzjCh6YVuelKG4nRUHb32/j9XFwdTW1Kv+20PYsZ5co
|
||||||
|
doPRty9F7Bg06yV0TJ6bOeYiUx9sR0rXSNvMRp7/fT8PPOZodnU1rkFN9/7CI4tf
|
||||||
|
BxaxjTezklBJGRJ3Vj12kGu9D1NFP5k5qrAT2jZfkRx8LOZIkhgfOaYp+0OPN4Q6
|
||||||
|
weWWX5ityl5iJWoovNd3cQzY/GtpouX8AzmiKfOiY/oQIyx6U8V0V2peNA1gZFtd
|
||||||
|
aHRBPWv6jH8p4KrqxKl+LtyBVl4JLg==
|
||||||
|
-----END X509 CRL-----
|
Binary file not shown.
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEsTCCApmgAwIBAgICC7kwDQYJKoZIhvcNAQELBQAwgYsxCzAJBgNVBAYTAlVT
|
||||||
|
MQswCQYDVQQIDAJNQTEPMA0GA1UEBwwGQm9zdG9uMRAwDgYDVQQKDAdSZWQgSGF0
|
||||||
|
MREwDwYDVQQLDAhLZXljbG9hazEUMBIGA1UEAwwLS2V5Y2xvYWsgQ0ExIzAhBgkq
|
||||||
|
hkiG9w0BCQEWFGNvbnRhY3RAa2V5Y2xvYWsub3JnMB4XDTE5MDMyMTE4MTk1MFoX
|
||||||
|
DTQ2MDgwNjE4MTk1MFowZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMRAwDgYD
|
||||||
|
VQQKDAdSZWQgSGF0MREwDwYDVQQLDAhLZXljbG9hazEjMCEGA1UEAwwaS2V5Y2xv
|
||||||
|
YWsgSW50ZXJtZWRpYXRlIENBIDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||||
|
AoIBAQD1afusL2MqMMxg20HTGyVt5nxc9MvpzU9yWhKyper9hh/iEDkH06SZ+vIn
|
||||||
|
UWGTeRfGmoA3W+IGF4TzSFO2BjOLz6bowNTqpWONZAK0swMauE23sbxA7XfKxmIt
|
||||||
|
5pDkQWAWb2kzLoKEXkbmdlF+O/qcM8i+1U7fpg1NjQI1DHvBVMvul/aGyzP6q/Hj
|
||||||
|
NEg+7o3y21T/3gq/Yma9L+awX1wLLxtDlSGjP1Q2C19w87ijUIwFUo7AzTOn03SI
|
||||||
|
t+Pfc1cdIsmjUG2lDqJTyo/VuqmfWWBMm6p+Ya/Z4Nipk87nxoW60y4EjRL7vxx9
|
||||||
|
vyWiRUhP/2pEE5y9LQ0uzxxA4kIPAgMBAAGjRTBDMB0GA1UdDgQWBBQYXI8JjLfL
|
||||||
|
FM+oCghx8t/R+2iv0zASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIB
|
||||||
|
hjANBgkqhkiG9w0BAQsFAAOCAgEAB5SrTyWz95GqgG0zrtIwJArMY4EUuISGWEgX
|
||||||
|
8GpVyP9xTM1he/vOXqFpAQHtygiq5yhyrrMJuu5/m0Ca5NZM1NaM6unJr7mIPEKK
|
||||||
|
BP/85Fue/gBe/Q0Vx1VnmxZhszqQT4hLIfR8anLXgK9w8E6lB4HAXgK+VUzzxXLE
|
||||||
|
43oc8/L9swEnvCKsLHKynexNxKRIZD3GPmLCciOz+101Fb6a0Nc8+A+5soiQi8gC
|
||||||
|
/K9ygXvxqmHtnrTndZmrtJFOaeXwMDAw7q3j25MkRt4fBPcKX0soBuI6/cTE2veH
|
||||||
|
GPYXW4QbVswrhJVD/vVuU0VFR1fLnuw9NgDkGz0qoi20x/Ew3HiVi++9EU9YsPgR
|
||||||
|
StcfsCTfth2TLsPmSFSKeGMR3e+Hr6xr6fcahSl6QmKcI60EHw+kKqQ3bBq7QOxi
|
||||||
|
zEuQ0SunRyuEUewnhMT9s7sIrZ4M2fyiWH3GVm2971J0/ey3NEYSq/Y9fAO6+9xG
|
||||||
|
Wlvaps8X5GLs/XYt9HwZNtdb9F3YMNSFi10WBIKqdpppLT9uuIDSK5S+s5chuych
|
||||||
|
y2TZiCBwKBt1lRdqEZ6J4vMj7M5eIdj8Pmvp+tmL1kewuTfKaU+2Bpy6l42sYkyM
|
||||||
|
jy7Z0XyMITIBnAf9SmElQPUjq+IIYL6HK/i8mI15AHHIiwm1eaUdbvBKJnQS0++b
|
||||||
|
0iwvQA4=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA9Wn7rC9jKjDMYNtB0xslbeZ8XPTL6c1PcloSsqXq/YYf4hA5
|
||||||
|
B9OkmfryJ1Fhk3kXxpqAN1viBheE80hTtgYzi8+m6MDU6qVjjWQCtLMDGrhNt7G8
|
||||||
|
QO13ysZiLeaQ5EFgFm9pMy6ChF5G5nZRfjv6nDPIvtVO36YNTY0CNQx7wVTL7pf2
|
||||||
|
hssz+qvx4zRIPu6N8ttU/94Kv2JmvS/msF9cCy8bQ5Uhoz9UNgtfcPO4o1CMBVKO
|
||||||
|
wM0zp9N0iLfj33NXHSLJo1BtpQ6iU8qP1bqpn1lgTJuqfmGv2eDYqZPO58aFutMu
|
||||||
|
BI0S+78cfb8lokVIT/9qRBOcvS0NLs8cQOJCDwIDAQABAoIBAQDMV7AH4fk3AyTa
|
||||||
|
LRa1GbBjvvukRuyXQ624MInLGN3+tTRM/mcOPkqbL9l7pYaSzcxfQPwrnCUqH2FD
|
||||||
|
VODm+mjnLEL1IMLokke/Thv2q+uUzwtfPe3bPh91xxOu1oGknU7Nv3yf8kUYxItS
|
||||||
|
kAgxDO4SLAgl5eTj0hbXkObalwdgpIIpobuiZUtJYRT1kJmmWJS8TDQirEyh298D
|
||||||
|
RdLHFB2wFgYkg9EjATM4tnv66YqPlW9YCkt1IlhqNyn+Ui8vo5shNXq1Q8x3p7he
|
||||||
|
ZdCqGL3Ddg9e9tZnOcFPCNQhFVIUV3T5e9SsPVimsJBiOzEzL0eYZ+8KyJogYeOq
|
||||||
|
yEPjqu0BAoGBAP4rGb1PE5xOg6WJ/PCXlFnCDpEqkXoZbDOPZn7cUl2OvzlIciTl
|
||||||
|
VgWt1HXq50zVfu0FHZx8PdmCj5av4wMmmcWvu7HZGTm4TErJ777M6c9IS/fKyL25
|
||||||
|
XBzw+HOcvCwq+pMTxKNZxAlNlTW0k9GwJeajXlzFyTDO5pTMiLrlJEOPAoGBAPcu
|
||||||
|
u283/CJMKE5YDgecYgwfaznX+Jk7jkiJpxfWWGYJUE3xueFROeqcwSI3WqRKm48N
|
||||||
|
hb9DC6yFtgO3uC7zUZFxOltkkkWSSB1PNmjLQl+y7E/UqrDEehIT/MhKXxUW9z4S
|
||||||
|
ADMZTZT0qeTlbBuwx6CAdnCAATRsW9peOziiKNmBAoGBAOLKtqbzHn6EmHdnjyln
|
||||||
|
N9p3i+QAZdrbQG8pb72W/m+45exJNoCxmnZqy3+EYWtvvVflDq0JN28UTueYfinb
|
||||||
|
ka6RxhtFqnqUdo7tbV2FHsP0sMSkT0brVMQGSMtweX+3werm4rkXahMbBR7syFF8
|
||||||
|
qfUIpTSGz6UbmSgA8ahCun8FAoGARLJiOUjP9CBCW3Oxgn/95+ybeloBp2Sb6KEJ
|
||||||
|
JWDW9JTGEsOJq4tNk1y5eG717A8oKJvTfhJ+HhaTPXlD4RiSpN9ZHqlW1asQC8VG
|
||||||
|
E93ZtosdjhpGzhXs7zVK3cd9oXjegguyroDrxOgyh4ETiKaa9Ip/YEjTDOTIqmnh
|
||||||
|
/51hyQECgYBJ3KwFfHlm77betQz9lvfU5uRBHjoIdor59tTXmvQ9ffdQtIuMN84w
|
||||||
|
2CbBuXrAD1hT2HRazfUncjI2Fj+pXD5+Hu42I31WxwNTdo/POznYu58MLog9RNGv
|
||||||
|
Avn456Fw8UmSkxm+zGnVJND9/0761/gOcabnIIm6KjI6WeACD1wJFA==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -94,7 +94,7 @@ keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||||
[ v3_intermediate_ca ]
|
[ v3_intermediate_ca ]
|
||||||
# Extensions for a typical intermediate CA (`man x509v3_config`).
|
# Extensions for a typical intermediate CA (`man x509v3_config`).
|
||||||
subjectKeyIdentifier = hash
|
subjectKeyIdentifier = hash
|
||||||
authorityKeyIdentifier = keyid:always,issuer
|
#authorityKeyIdentifier = keyid:always,issuer
|
||||||
basicConstraints = critical, CA:true, pathlen:0
|
basicConstraints = critical, CA:true, pathlen:0
|
||||||
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ basicConstraints = CA:FALSE
|
||||||
nsCertType = client, email
|
nsCertType = client, email
|
||||||
nsComment = "OpenSSL Generated Client Certificate"
|
nsComment = "OpenSSL Generated Client Certificate"
|
||||||
subjectKeyIdentifier = hash
|
subjectKeyIdentifier = hash
|
||||||
authorityKeyIdentifier = keyid,issuer
|
#authorityKeyIdentifier = keyid,issuer
|
||||||
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
|
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
|
||||||
extendedKeyUsage = clientAuth, emailProtection
|
extendedKeyUsage = clientAuth, emailProtection
|
||||||
|
|
||||||
|
|
|
@ -190,8 +190,7 @@
|
||||||
<include>ca.crt</include>
|
<include>ca.crt</include>
|
||||||
<include>client.crt</include>
|
<include>client.crt</include>
|
||||||
<include>client.key</include>
|
<include>client.key</include>
|
||||||
<include>intermediate-ca.crl</include>
|
<include>*.crl</include>
|
||||||
<include>empty.crl</include>
|
|
||||||
<!-- KEYCLOAK-6771 Certificate Bound Token -->
|
<!-- KEYCLOAK-6771 Certificate Bound Token -->
|
||||||
<include>other_client.jks</include>
|
<include>other_client.jks</include>
|
||||||
</includes>
|
</includes>
|
||||||
|
|
|
@ -92,6 +92,8 @@ public abstract class AbstractX509AuthenticationTest extends AbstractTestRealmKe
|
||||||
|
|
||||||
public static final String EMPTY_CRL_PATH = "empty.crl";
|
public static final String EMPTY_CRL_PATH = "empty.crl";
|
||||||
public static final String INTERMEDIATE_CA_CRL_PATH = "intermediate-ca.crl";
|
public static final String INTERMEDIATE_CA_CRL_PATH = "intermediate-ca.crl";
|
||||||
|
public static final String INTERMEDIATE_CA_INVALID_SIGNATURE_CRL_PATH = "intermediate-ca-invalid-signature.crl";
|
||||||
|
public static final String INTERMEDIATE_CA_3_CRL_PATH = "intermediate-ca-3.crl";
|
||||||
protected final Logger log = Logger.getLogger(this.getClass());
|
protected final Logger log = Logger.getLogger(this.getClass());
|
||||||
|
|
||||||
static final String REQUIRED = "REQUIRED";
|
static final String REQUIRED = "REQUIRED";
|
||||||
|
|
|
@ -58,6 +58,8 @@ public class CRLRule extends ExternalResource {
|
||||||
PathHandler pathHandler = new PathHandler();
|
PathHandler pathHandler = new PathHandler();
|
||||||
pathHandler.addExactPath(AbstractX509AuthenticationTest.EMPTY_CRL_PATH, new CRLHandler(AbstractX509AuthenticationTest.EMPTY_CRL_PATH));
|
pathHandler.addExactPath(AbstractX509AuthenticationTest.EMPTY_CRL_PATH, new CRLHandler(AbstractX509AuthenticationTest.EMPTY_CRL_PATH));
|
||||||
pathHandler.addExactPath(AbstractX509AuthenticationTest.INTERMEDIATE_CA_CRL_PATH, new CRLHandler(AbstractX509AuthenticationTest.INTERMEDIATE_CA_CRL_PATH));
|
pathHandler.addExactPath(AbstractX509AuthenticationTest.INTERMEDIATE_CA_CRL_PATH, new CRLHandler(AbstractX509AuthenticationTest.INTERMEDIATE_CA_CRL_PATH));
|
||||||
|
pathHandler.addExactPath(AbstractX509AuthenticationTest.INTERMEDIATE_CA_INVALID_SIGNATURE_CRL_PATH, new CRLHandler(AbstractX509AuthenticationTest.INTERMEDIATE_CA_INVALID_SIGNATURE_CRL_PATH));
|
||||||
|
pathHandler.addExactPath(AbstractX509AuthenticationTest.INTERMEDIATE_CA_3_CRL_PATH, new CRLHandler(AbstractX509AuthenticationTest.INTERMEDIATE_CA_3_CRL_PATH));
|
||||||
|
|
||||||
crlResponder = Undertow.builder().addHttpListener(CRL_RESPONDER_PORT, CRL_RESPONDER_HOST)
|
crlResponder = Undertow.builder().addHttpListener(CRL_RESPONDER_PORT, CRL_RESPONDER_HOST)
|
||||||
.setHandler(
|
.setHandler(
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.util.ContainerAssume;
|
import org.keycloak.testsuite.util.ContainerAssume;
|
||||||
import org.keycloak.testsuite.util.PhantomJSBrowser;
|
import org.keycloak.testsuite.util.PhantomJSBrowser;
|
||||||
|
import org.keycloak.testsuite.util.WaitUtils;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
@ -121,6 +122,42 @@ public class X509BrowserCRLTest extends AbstractX509AuthenticationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginFailedWithInvalidSignatureCRL() {
|
||||||
|
X509AuthenticatorConfigModel config =
|
||||||
|
new X509AuthenticatorConfigModel()
|
||||||
|
.setCRLEnabled(true)
|
||||||
|
.setCRLRelativePath(CRLRule.CRL_RESPONDER_ORIGIN + "/" + INTERMEDIATE_CA_INVALID_SIGNATURE_CRL_PATH)
|
||||||
|
.setConfirmationPageAllowed(true)
|
||||||
|
.setMappingSourceType(SUBJECTDN_EMAIL)
|
||||||
|
.setUserIdentityMapperType(USERNAME_EMAIL);
|
||||||
|
AuthenticatorConfigRepresentation cfg = newConfig("x509-browser-config", config.getConfig());
|
||||||
|
String cfgId = createConfig(browserExecution.getId(), cfg);
|
||||||
|
Assert.assertNotNull(cfgId);
|
||||||
|
|
||||||
|
// Verify there is an error message because of invalid CRL signature
|
||||||
|
assertLoginFailedWithExpectedX509Error("Certificate validation's failed.\nSignature length not correct");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginSuccessWithCRLSignedWithIntermediateCA3FromTruststore() {
|
||||||
|
X509AuthenticatorConfigModel config =
|
||||||
|
new X509AuthenticatorConfigModel()
|
||||||
|
.setCRLEnabled(true)
|
||||||
|
.setCRLRelativePath(CRLRule.CRL_RESPONDER_ORIGIN + "/" + INTERMEDIATE_CA_3_CRL_PATH)
|
||||||
|
.setConfirmationPageAllowed(true)
|
||||||
|
.setMappingSourceType(SUBJECTDN_EMAIL)
|
||||||
|
.setUserIdentityMapperType(USERNAME_EMAIL);
|
||||||
|
AuthenticatorConfigRepresentation cfg = newConfig("x509-browser-config", config.getConfig());
|
||||||
|
String cfgId = createConfig(browserExecution.getId(), cfg);
|
||||||
|
Assert.assertNotNull(cfgId);
|
||||||
|
|
||||||
|
// Verify there is an error message because of invalid CRL signature
|
||||||
|
x509BrowserLogin(config, userId, "test-user@localhost", "test-user@localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginWithMultipleRevocationLists() {
|
public void loginWithMultipleRevocationLists() {
|
||||||
X509AuthenticatorConfigModel config =
|
X509AuthenticatorConfigModel config =
|
||||||
|
@ -157,13 +194,17 @@ public class X509BrowserCRLTest extends AbstractX509AuthenticationTest {
|
||||||
|
|
||||||
|
|
||||||
private void assertLoginFailedDueRevokedCertificate() {
|
private void assertLoginFailedDueRevokedCertificate() {
|
||||||
|
assertLoginFailedWithExpectedX509Error("Certificate validation's failed.\nCertificate has been revoked, certificate's subject:");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLoginFailedWithExpectedX509Error(String expectedError) {
|
||||||
loginConfirmationPage.open();
|
loginConfirmationPage.open();
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
// Verify there is an error message
|
// Verify there is an error message
|
||||||
Assert.assertNotNull(loginPage.getError());
|
Assert.assertNotNull(loginPage.getError());
|
||||||
|
|
||||||
Assert.assertThat(loginPage.getError(), containsString("Certificate validation's failed.\nCertificate has been revoked, certificate's subject:"));
|
Assert.assertThat(loginPage.getError(), containsString(expectedError));
|
||||||
|
|
||||||
// Continue with form based login
|
// Continue with form based login
|
||||||
loginPage.login("test-user@localhost", "password");
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
@ -177,5 +218,4 @@ public class X509BrowserCRLTest extends AbstractX509AuthenticationTest {
|
||||||
.removeDetail(Details.REDIRECT_URI)
|
.removeDetail(Details.REDIRECT_URI)
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in a new issue