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

@ -59,25 +59,25 @@ import org.keycloak.truststore.TruststoreProviderFactory;
public class NginxProxySslClientCertificateLookup extends AbstractClientCertificateFromHttpHeadersLookup { 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 boolean isTruststoreLoaded = false;
private static KeyStore truststore = null; private static KeyStore truststore = null;
private static Set<X509Certificate> trustedRootCerts = null; private static Set<X509Certificate> trustedRootCerts = null;
private static Set<X509Certificate> intermediateCerts = null; private static Set<X509Certificate> intermediateCerts = null;
public NginxProxySslClientCertificateLookup(String sslCientCertHttpHeader, public NginxProxySslClientCertificateLookup(String sslCientCertHttpHeader,
String sslCertChainHttpHeaderPrefix, String sslCertChainHttpHeaderPrefix,
int certificateChainLength, int certificateChainLength,
KeycloakSession kcsession) { KeycloakSession kcsession) {
super(sslCientCertHttpHeader, sslCertChainHttpHeaderPrefix, certificateChainLength); 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("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"); log.warn(" see Keycloak documentation here : https://www.keycloak.org/docs/latest/server_installation/index.html#_truststore");
} }
} }
/** /**
@ -101,14 +101,14 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
protected X509Certificate decodeCertificateFromPem(String pem) throws PemException { protected X509Certificate decodeCertificateFromPem(String pem) throws PemException {
if (pem == null) { if (pem == null) {
log.warn("End user TLS Certificate is NULL! "); log.warn("End user TLS Certificate is NULL! ");
return null; return null;
} }
try { try {
pem = java.net.URLDecoder.decode(pem, "UTF-8"); pem = java.net.URLDecoder.decode(pem, "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
log.error("Cannot URL decode the end user TLS Certificate : " + pem,e); log.error("Cannot URL decode the end user TLS Certificate : " + pem,e);
} }
if (pem.startsWith("-----BEGIN CERTIFICATE-----")) { if (pem.startsWith("-----BEGIN CERTIFICATE-----")) {
pem = removeBeginEnd(pem); pem = removeBeginEnd(pem);
@ -127,16 +127,16 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
if (clientCert != null) { if (clientCert != null) {
log.debugf("End user certificate found : Subject DN=[%s] SerialNumber=[%s]", clientCert.getSubjectDN(), clientCert.getSerialNumber()); 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); X509Certificate[] certChain = buildChain(clientCert);
if ( certChain == null || certChain.length == 0 ) { if (certChain == null || certChain.length == 0) {
log.info("Impossible to rebuild end user cert chain : client certificate authentication will fail." ); log.info("Impossible to rebuild end user cert chain : client certificate authentication will fail." );
chain.add(clientCert); chain.add(clientCert);
} else { } else {
for (X509Certificate cacert : certChain) { for (X509Certificate cacert : certChain) {
chain.add(cacert); chain.add(cacert);
log.debugf("Rebuilded user cert chain DN : %s", cacert.getSubjectDN().toString() ); log.debugf("Rebuilded user cert chain DN : %s", cacert.getSubjectDN().toString() );
} }
} }
} }
return chain.toArray(new X509Certificate[0]); return chain.toArray(new X509Certificate[0]);
@ -150,14 +150,14 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
* @param end_user_auth_cert * @param end_user_auth_cert
* @return * @return
*/ */
public X509Certificate[] buildChain(X509Certificate end_user_auth_cert) { public X509Certificate[] buildChain(X509Certificate end_user_auth_cert) {
X509Certificate[] user_cert_chain = null; X509Certificate[] user_cert_chain = null;
try { try {
// No truststore : no way! // No truststore : no way!
if (truststore == null) { if (isTruststoreLoaded == false) {
log.warn("Keycloak Truststore is null, but it is required !"); log.warn("Keycloak Truststore is null, but it is required !");
log.warn(" see https://www.keycloak.org/docs/latest/server_installation/index.html#_truststore"); log.warn(" see https://www.keycloak.org/docs/latest/server_installation/index.html#_truststore");
return null; return null;
@ -196,66 +196,71 @@ public class NginxProxySslClientCertificateLookup extends AbstractClientCertific
user_cert_chain = convertCertPathtoX509CertArray( certPath ); user_cert_chain = convertCertPathtoX509CertArray( certPath );
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
log.error(e.getLocalizedMessage(),e); log.error(e.getLocalizedMessage(),e);
} catch (CertPathBuilderException e) { } catch (CertPathBuilderException e) {
if ( log.isEnabled(Level.TRACE) ) if (log.isEnabled(Level.TRACE)) {
log.debug(e.getLocalizedMessage(),e); log.debug(e.getLocalizedMessage(),e);
else } else {
log.warn(e.getLocalizedMessage()); log.warn(e.getLocalizedMessage());
}
} catch (InvalidAlgorithmParameterException e) { } catch (InvalidAlgorithmParameterException e) {
log.error(e.getLocalizedMessage(),e); log.error(e.getLocalizedMessage(),e);
} catch (NoSuchProviderException e) { } catch (NoSuchProviderException e) {
log.error(e.getLocalizedMessage(),e); log.error(e.getLocalizedMessage(),e);
} finally { } finally {
//Remove end user certificate if (isTruststoreLoaded) {
intermediateCerts.remove(end_user_auth_cert); //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;
}
} }
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;
}
} }