KEYCLOAK-18852 Prevent NPE in case of missing truststore

even though the "return null" at the top of the method is called if no truststore is set, the finally block is still executed. And since the keystore is not there an NPE is thrown when calling the remove method.
This commit is contained in:
Robert Schuh 2021-07-19 18:45:21 +02:00 committed by Marek Posolda
parent d29d945cc4
commit 843bbf1bb3

View file

@ -34,10 +34,10 @@ import org.keycloak.truststore.TruststoreProviderFactory;
/**
* The NGINX Provider extract end user X.509 certificate send during TLS mutual authentication,
* and forwarded in an http header.
*
*
* NGINX configuration must have :
* <code>
* server {
* server {
* ...
* ssl_client_certificate path-to-my-trustyed-cas-for-client-auth.pem;
* ssl_verify_client on|optional_no_ca;
@ -49,9 +49,9 @@ import org.keycloak.truststore.TruststoreProviderFactory;
* ...
* }
* </code>
*
*
* Note that $ssl_client_cert is deprecated, use only $ssl_client_escaped_cert with this implementation
*
*
* @author <a href="mailto:arnault.michel@toad-consulting.com">Arnault MICHEL</a>
* @version $Revision: 1 $
* @since 10/09/2018
@ -59,30 +59,30 @@ import org.keycloak.truststore.TruststoreProviderFactory;
public class NginxProxySslClientCertificateLookup extends AbstractClientCertificateFromHttpHeadersLookup {
private static final Logger log = Logger.getLogger(NginxProxySslClientCertificateLookup.class);
private static final Logger log = Logger.getLogger(NginxProxySslClientCertificateLookup.class);
private static boolean isTruststoreLoaded = false;
private static KeyStore truststore = null;
private static Set<X509Certificate> trustedRootCerts = null;
private static Set<X509Certificate> intermediateCerts = null;
private static boolean isTruststoreLoaded = false;
private static KeyStore truststore = null;
private static Set<X509Certificate> trustedRootCerts = null;
private static Set<X509Certificate> intermediateCerts = null;
public NginxProxySslClientCertificateLookup(String sslCientCertHttpHeader,
String sslCertChainHttpHeaderPrefix,
int certificateChainLength,
KeycloakSession kcsession) {
String sslCertChainHttpHeaderPrefix,
int certificateChainLength,
KeycloakSession kcsession) {
super(sslCientCertHttpHeader, sslCertChainHttpHeaderPrefix, certificateChainLength);
if (!loadKeycloakTrustStore(kcsession)) {
if (!loadKeycloakTrustStore(kcsession)) {
log.warn("Keycloak Truststore is null or empty, but it's required for NGINX x509cert-lookup provider");
log.warn(" see Keycloak documentation here : https://www.keycloak.org/docs/latest/server_installation/index.html#_truststore");
}
}
}
/**
* Removing PEM Headers and end of lines
*
*
* @param pem
* @return
*/
@ -101,15 +101,15 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
protected X509Certificate decodeCertificateFromPem(String pem) throws PemException {
if (pem == null) {
log.warn("End user TLS Certificate is 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);
}
try {
pem = java.net.URLDecoder.decode(pem, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error("Cannot URL decode the end user TLS Certificate : " + pem,e);
}
if (pem.startsWith("-----BEGIN CERTIFICATE-----")) {
pem = removeBeginEnd(pem);
}
@ -127,16 +127,16 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
if (clientCert != null) {
log.debugf("End user certificate found : Subject DN=[%s] SerialNumber=[%s]", clientCert.getSubjectDN(), clientCert.getSerialNumber());
// Rebuilding the end user certificate chain using Keycloak Truststore
// Rebuilding the end user certificate chain using Keycloak Truststore
X509Certificate[] certChain = buildChain(clientCert);
if ( certChain == null || certChain.length == 0 ) {
log.info("Impossible to rebuild end user cert chain : client certificate authentication will fail." );
chain.add(clientCert);
if (certChain == null || certChain.length == 0) {
log.info("Impossible to rebuild end user cert chain : client certificate authentication will fail." );
chain.add(clientCert);
} else {
for (X509Certificate cacert : certChain) {
chain.add(cacert);
log.debugf("Rebuilded user cert chain DN : %s", cacert.getSubjectDN().toString() );
}
for (X509Certificate cacert : certChain) {
chain.add(cacert);
log.debugf("Rebuilded user cert chain DN : %s", cacert.getSubjectDN().toString() );
}
}
}
return chain.toArray(new X509Certificate[0]);
@ -150,14 +150,14 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
* @param end_user_auth_cert
* @return
*/
public X509Certificate[] buildChain(X509Certificate end_user_auth_cert) {
X509Certificate[] user_cert_chain = null;
public X509Certificate[] buildChain(X509Certificate end_user_auth_cert) {
X509Certificate[] user_cert_chain = null;
try {
// No truststore : no way!
if (truststore == null) {
// No truststore : no way!
if (isTruststoreLoaded == false) {
log.warn("Keycloak Truststore is null, but it is required !");
log.warn(" see https://www.keycloak.org/docs/latest/server_installation/index.html#_truststore");
return null;
@ -174,14 +174,14 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
}
// Configure the PKIX certificate builder algorithm parameters
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters( trustAnchors, selector);
// Disable CRL checks, as it's possibly done after depending on Keycloak settings
pkixParams.setRevocationEnabled(false);
pkixParams.setExplicitPolicyRequired(false);
pkixParams.setAnyPolicyInhibited(false);
pkixParams.setPolicyQualifiersRejected(false);
pkixParams.setMaxPathLength(certificateChainLength);
// Adding the list of intermediate certificates + end user certificate
intermediateCerts.add(end_user_auth_cert);
CollectionCertStoreParameters intermediateCA_userCert = new CollectionCertStoreParameters(intermediateCerts);
@ -192,70 +192,75 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX","BC");
CertPath certPath = certPathBuilder.build(pkixParams).getCertPath();
log.debug("Certification path building OK, and contains " + certPath.getCertificates().size() + " X509 Certificates");
user_cert_chain = convertCertPathtoX509CertArray( certPath );
} catch (NoSuchAlgorithmException e) {
log.error(e.getLocalizedMessage(),e);
log.error(e.getLocalizedMessage(),e);
} catch (CertPathBuilderException e) {
if ( log.isEnabled(Level.TRACE) )
log.debug(e.getLocalizedMessage(),e);
else
log.warn(e.getLocalizedMessage());
if (log.isEnabled(Level.TRACE)) {
log.debug(e.getLocalizedMessage(),e);
} else {
log.warn(e.getLocalizedMessage());
}
} catch (InvalidAlgorithmParameterException e) {
log.error(e.getLocalizedMessage(),e);
log.error(e.getLocalizedMessage(),e);
} catch (NoSuchProviderException e) {
log.error(e.getLocalizedMessage(),e);
} finally {
//Remove end user certificate
intermediateCerts.remove(end_user_auth_cert);
}
return user_cert_chain;
}
public X509Certificate[] convertCertPathtoX509CertArray( CertPath certPath ) {
X509Certificate[] x509certchain = null;
if (certPath!=null) {
List<X509Certificate> trustedX509Chain = new ArrayList<X509Certificate>();
for (Certificate certificate : certPath.getCertificates() )
if ( certificate instanceof X509Certificate )
trustedX509Chain.add((X509Certificate)certificate);
x509certchain = trustedX509Chain.toArray(new X509Certificate[0]);
}
return x509certchain;
}
/** Loading truststore @ first login
*
* @param kcsession
* @return
*/
public boolean loadKeycloakTrustStore(KeycloakSession kcsession) {
if (!isTruststoreLoaded) {
log.debug(" Loading Keycloak truststore ...");
KeycloakSessionFactory factory = kcsession.getKeycloakSessionFactory();
TruststoreProviderFactory truststoreFactory = (TruststoreProviderFactory) factory.getProviderFactory(TruststoreProvider.class, "file");
TruststoreProvider provider = truststoreFactory.create(kcsession);
if ( provider != null && provider.getTruststore() != null ) {
truststore = provider.getTruststore();
trustedRootCerts = new HashSet<>(provider.getRootCertificates().values());
intermediateCerts = new HashSet<>(provider.getIntermediateCertificates().values());
log.debug("Keycloak truststore loaded for NGINX x509cert-lookup provider.");
isTruststoreLoaded = true;
}
log.error(e.getLocalizedMessage(),e);
} finally {
if (isTruststoreLoaded) {
//Remove end user certificate
intermediateCerts.remove(end_user_auth_cert);
}
}
return isTruststoreLoaded;
}
return user_cert_chain;
}
public X509Certificate[] convertCertPathtoX509CertArray( CertPath certPath ) {
X509Certificate[] x509certchain = null;
if (certPath != null) {
List<X509Certificate> trustedX509Chain = new ArrayList<X509Certificate>();
for (Certificate certificate : certPath.getCertificates()) {
if (certificate instanceof X509Certificate) {
trustedX509Chain.add((X509Certificate) certificate);
}
}
x509certchain = trustedX509Chain.toArray(new X509Certificate[0]);
}
return x509certchain;
}
/** Loading truststore @ first login
*
* @param kcsession
* @return
*/
public boolean loadKeycloakTrustStore(KeycloakSession kcsession) {
if (!isTruststoreLoaded) {
log.debug(" Loading Keycloak truststore ...");
KeycloakSessionFactory factory = kcsession.getKeycloakSessionFactory();
TruststoreProviderFactory truststoreFactory = (TruststoreProviderFactory) factory.getProviderFactory(TruststoreProvider.class, "file");
TruststoreProvider provider = truststoreFactory.create(kcsession);
if (provider != null && provider.getTruststore() != null) {
truststore = provider.getTruststore();
trustedRootCerts = new HashSet<>(provider.getRootCertificates().values());
intermediateCerts = new HashSet<>(provider.getIntermediateCertificates().values());
log.debug("Keycloak truststore loaded for NGINX x509cert-lookup provider.");
isTruststoreLoaded = true;
}
}
return isTruststoreLoaded;
}
}