Skip to content

Commit ab02bbe

Browse files
authored
Replace custom reloadable Key/TrustManager (#30509) (#30639)
Make SSLContext reloadable This commit replaces all customKeyManagers and TrustManagers (ReloadableKeyManager,ReloadableTrustManager, EmptyKeyManager, EmptyTrustManager) with instances of X509ExtendedKeyManager and X509ExtendedTrustManager. This change was triggered by the effort to allow Elasticsearch to run in a FIPS-140 environment. In JVMs running in FIPS approved mode, only SunJSSE TrustManagers and KeyManagers can be used. Reloadability is now ensured by a volatile instance of SSLContext in SSLContectHolder. SSLConfigurationReloaderTests use the reloadable SSLContext to initialize HTTP Clients and Servers and use these for testing the key material and trust relations.
1 parent 24948aa commit ab02bbe

File tree

3 files changed

+318
-524
lines changed

3 files changed

+318
-524
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLService.java

+51-248
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.apache.http.conn.ssl.NoopHostnameVerifier;
99
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
1010
import org.apache.lucene.util.SetOnce;
11-
import org.bouncycastle.operator.OperatorCreationException;
1211
import org.elasticsearch.ElasticsearchException;
1312
import org.elasticsearch.common.CheckedSupplier;
1413
import org.elasticsearch.common.Strings;
@@ -21,28 +20,24 @@
2120
import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
2221

2322
import javax.net.ssl.HostnameVerifier;
23+
import javax.net.ssl.KeyManagerFactory;
2424
import javax.net.ssl.SSLContext;
2525
import javax.net.ssl.SSLEngine;
2626
import javax.net.ssl.SSLParameters;
2727
import javax.net.ssl.SSLSessionContext;
2828
import javax.net.ssl.SSLSocket;
2929
import javax.net.ssl.SSLSocketFactory;
30+
import javax.net.ssl.TrustManagerFactory;
3031
import javax.net.ssl.X509ExtendedKeyManager;
3132
import javax.net.ssl.X509ExtendedTrustManager;
32-
import javax.security.auth.DestroyFailedException;
3333

3434
import java.io.IOException;
3535
import java.net.InetAddress;
3636
import java.net.Socket;
3737
import java.security.GeneralSecurityException;
3838
import java.security.KeyManagementException;
39-
import java.security.KeyStoreException;
39+
import java.security.KeyStore;
4040
import java.security.NoSuchAlgorithmException;
41-
import java.security.Principal;
42-
import java.security.PrivateKey;
43-
import java.security.UnrecoverableKeyException;
44-
import java.security.cert.CertificateException;
45-
import java.security.cert.X509Certificate;
4641
import java.util.ArrayList;
4742
import java.util.Arrays;
4843
import java.util.Collection;
@@ -54,6 +49,7 @@
5449
import java.util.List;
5550
import java.util.Map;
5651
import java.util.Map.Entry;
52+
import java.util.Optional;
5753
import java.util.Set;
5854

5955
/**
@@ -71,8 +67,7 @@ public class SSLService extends AbstractComponent {
7167
* Create a new SSLService that parses the settings for the ssl contexts that need to be created, creates them, and then caches them
7268
* for use later
7369
*/
74-
public SSLService(Settings settings, Environment environment) throws CertificateException, UnrecoverableKeyException,
75-
NoSuchAlgorithmException, IOException, DestroyFailedException, KeyStoreException, OperatorCreationException {
70+
public SSLService(Settings settings, Environment environment) {
7671
super(settings);
7772
this.env = environment;
7873
this.globalSSLConfiguration = new SSLConfiguration(settings.getByPrefix(XPackSettings.GLOBAL_SSL_PREFIX));
@@ -403,10 +398,8 @@ private SSLContextHolder createSslContext(SSLConfiguration sslConfiguration) {
403398
if (logger.isDebugEnabled()) {
404399
logger.debug("using ssl settings [{}]", sslConfiguration);
405400
}
406-
ReloadableTrustManager trustManager =
407-
new ReloadableTrustManager(sslConfiguration.trustConfig().createTrustManager(env), sslConfiguration.trustConfig());
408-
ReloadableX509KeyManager keyManager =
409-
new ReloadableX509KeyManager(sslConfiguration.keyConfig().createKeyManager(env), sslConfiguration.keyConfig());
401+
X509ExtendedTrustManager trustManager = sslConfiguration.trustConfig().createTrustManager(env);
402+
X509ExtendedKeyManager keyManager = sslConfiguration.keyConfig().createKeyManager(env);
410403
return createSslContext(keyManager, trustManager, sslConfiguration);
411404
}
412405

@@ -417,7 +410,7 @@ private SSLContextHolder createSslContext(SSLConfiguration sslConfiguration) {
417410
* @param trustManager the trust manager to use
418411
* @return the created SSLContext
419412
*/
420-
private SSLContextHolder createSslContext(ReloadableX509KeyManager keyManager, ReloadableTrustManager trustManager,
413+
private SSLContextHolder createSslContext(X509ExtendedKeyManager keyManager, X509ExtendedTrustManager trustManager,
421414
SSLConfiguration sslConfiguration) {
422415
// Initialize sslContext
423416
try {
@@ -427,7 +420,7 @@ private SSLContextHolder createSslContext(ReloadableX509KeyManager keyManager, R
427420
// check the supported ciphers and log them here to prevent spamming logs on every call
428421
supportedCiphers(sslContext.getSupportedSSLParameters().getCipherSuites(), sslConfiguration.cipherSuites(), true);
429422

430-
return new SSLContextHolder(sslContext, trustManager, keyManager);
423+
return new SSLContextHolder(sslContext, sslConfiguration);
431424
} catch (NoSuchAlgorithmException | KeyManagementException e) {
432425
throw new ElasticsearchException("failed to initialize the SSLContext", e);
433426
}
@@ -436,9 +429,7 @@ private SSLContextHolder createSslContext(ReloadableX509KeyManager keyManager, R
436429
/**
437430
* Parses the settings to load all SSLConfiguration objects that will be used.
438431
*/
439-
Map<SSLConfiguration, SSLContextHolder> loadSSLConfigurations() throws CertificateException,
440-
UnrecoverableKeyException, NoSuchAlgorithmException, IOException, DestroyFailedException, KeyStoreException,
441-
OperatorCreationException {
432+
Map<SSLConfiguration, SSLContextHolder> loadSSLConfigurations() {
442433
Map<SSLConfiguration, SSLContextHolder> sslConfigurations = new HashMap<>();
443434
sslConfigurations.put(globalSSLConfiguration, createSslContext(globalSSLConfiguration));
444435

@@ -560,258 +551,70 @@ private static SSLSocket createWithPermissions(CheckedSupplier<Socket, IOExcepti
560551
}
561552
}
562553

563-
/**
564-
* Wraps a trust manager to delegate to. If the trust material needs to be reloaded, then the delegate will be switched after
565-
* reloading
566-
*/
567-
final class ReloadableTrustManager extends X509ExtendedTrustManager {
568-
569-
private volatile X509ExtendedTrustManager trustManager;
570-
private final TrustConfig trustConfig;
571-
572-
ReloadableTrustManager(X509ExtendedTrustManager trustManager, TrustConfig trustConfig) {
573-
this.trustManager = trustManager == null ? new EmptyX509TrustManager() : trustManager;
574-
this.trustConfig = trustConfig;
575-
}
576-
577-
@Override
578-
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
579-
trustManager.checkClientTrusted(x509Certificates, s, socket);
580-
}
581-
582-
@Override
583-
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
584-
trustManager.checkServerTrusted(x509Certificates, s, socket);
585-
}
586-
587-
@Override
588-
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
589-
trustManager.checkClientTrusted(x509Certificates, s, sslEngine);
590-
}
591-
592-
@Override
593-
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
594-
trustManager.checkServerTrusted(x509Certificates, s, sslEngine);
595-
}
596-
597-
@Override
598-
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
599-
trustManager.checkClientTrusted(x509Certificates, s);
600-
}
601-
602-
@Override
603-
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
604-
trustManager.checkServerTrusted(x509Certificates, s);
605-
}
606-
607-
@Override
608-
public X509Certificate[] getAcceptedIssuers() {
609-
return trustManager.getAcceptedIssuers();
610-
}
611-
612-
void reload() {
613-
X509ExtendedTrustManager loadedTrustManager = trustConfig.createTrustManager(env);
614-
if (loadedTrustManager == null) {
615-
this.trustManager = new EmptyX509TrustManager();
616-
} else {
617-
this.trustManager = loadedTrustManager;
618-
}
619-
}
620-
621-
X509ExtendedTrustManager getTrustManager() {
622-
return trustManager;
623-
}
624-
}
625-
626-
/**
627-
* Wraps a key manager and delegates all calls to it. When the key material needs to be reloaded, then the delegate is swapped after
628-
* a new one has been loaded
629-
*/
630-
final class ReloadableX509KeyManager extends X509ExtendedKeyManager {
631554

632-
private volatile X509ExtendedKeyManager keyManager;
555+
final class SSLContextHolder {
556+
private volatile SSLContext context;
633557
private final KeyConfig keyConfig;
558+
private final TrustConfig trustConfig;
559+
private final SSLConfiguration sslConfiguration;
634560

635-
ReloadableX509KeyManager(X509ExtendedKeyManager keyManager, KeyConfig keyConfig) {
636-
this.keyManager = keyManager == null ? new EmptyKeyManager() : keyManager;
637-
this.keyConfig = keyConfig;
638-
}
639-
640-
@Override
641-
public String[] getClientAliases(String s, Principal[] principals) {
642-
return keyManager.getClientAliases(s, principals);
643-
}
644-
645-
@Override
646-
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
647-
return keyManager.chooseClientAlias(strings, principals, socket);
648-
}
649-
650-
@Override
651-
public String[] getServerAliases(String s, Principal[] principals) {
652-
return keyManager.getServerAliases(s, principals);
653-
}
654-
655-
@Override
656-
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
657-
return keyManager.chooseServerAlias(s, principals, socket);
658-
}
659-
660-
@Override
661-
public X509Certificate[] getCertificateChain(String s) {
662-
return keyManager.getCertificateChain(s);
663-
}
664-
665-
@Override
666-
public PrivateKey getPrivateKey(String s) {
667-
return keyManager.getPrivateKey(s);
668-
}
669-
670-
@Override
671-
public String chooseEngineClientAlias(String[] strings, Principal[] principals, SSLEngine engine) {
672-
return keyManager.chooseEngineClientAlias(strings, principals, engine);
673-
}
674-
675-
@Override
676-
public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine engine) {
677-
return keyManager.chooseEngineServerAlias(s, principals, engine);
678-
}
679-
680-
void reload() {
681-
X509ExtendedKeyManager loadedKeyManager = keyConfig.createKeyManager(env);
682-
if (loadedKeyManager == null) {
683-
this.keyManager = new EmptyKeyManager();
684-
} else {
685-
this.keyManager = loadedKeyManager;
686-
}
687-
}
688-
689-
// pkg-private accessor for testing
690-
X509ExtendedKeyManager getKeyManager() {
691-
return keyManager;
692-
}
693-
}
694-
695-
/**
696-
* A struct for holding the SSLContext and the backing key manager and trust manager
697-
*/
698-
static final class SSLContextHolder {
699-
700-
private final SSLContext context;
701-
private final ReloadableTrustManager trustManager;
702-
private final ReloadableX509KeyManager keyManager;
703-
704-
SSLContextHolder(SSLContext context, ReloadableTrustManager trustManager, ReloadableX509KeyManager keyManager) {
561+
SSLContextHolder(SSLContext context, SSLConfiguration sslConfiguration) {
705562
this.context = context;
706-
this.trustManager = trustManager;
707-
this.keyManager = keyManager;
563+
this.sslConfiguration = sslConfiguration;
564+
this.keyConfig = sslConfiguration.keyConfig();
565+
this.trustConfig = sslConfiguration.trustConfig();
708566
}
709567

710568
SSLContext sslContext() {
711569
return context;
712570
}
713571

714-
ReloadableX509KeyManager keyManager() {
715-
return keyManager;
716-
}
717-
718-
ReloadableTrustManager trustManager() {
719-
return trustManager;
720-
}
721-
722-
synchronized void reload() {
723-
trustManager.reload();
724-
keyManager.reload();
725-
invalidateSessions(context.getClientSessionContext());
726-
invalidateSessions(context.getServerSessionContext());
727-
}
728-
729572
/**
730573
* Invalidates the sessions in the provided {@link SSLSessionContext}
731574
*/
732-
private static void invalidateSessions(SSLSessionContext sslSessionContext) {
575+
private void invalidateSessions(SSLSessionContext sslSessionContext) {
733576
Enumeration<byte[]> sessionIds = sslSessionContext.getIds();
734577
while (sessionIds.hasMoreElements()) {
735578
byte[] sessionId = sessionIds.nextElement();
736579
sslSessionContext.getSession(sessionId).invalidate();
737580
}
738581
}
739-
}
740-
741-
/**
742-
* This is an empty key manager that is used in case a loaded key manager is null
743-
*/
744-
private static final class EmptyKeyManager extends X509ExtendedKeyManager {
745-
746-
@Override
747-
public String[] getClientAliases(String s, Principal[] principals) {
748-
return new String[0];
749-
}
750-
751-
@Override
752-
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
753-
return null;
754-
}
755-
756-
@Override
757-
public String[] getServerAliases(String s, Principal[] principals) {
758-
return new String[0];
759-
}
760-
761-
@Override
762-
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
763-
return null;
764-
}
765-
766-
@Override
767-
public X509Certificate[] getCertificateChain(String s) {
768-
return new X509Certificate[0];
769-
}
770-
771-
@Override
772-
public PrivateKey getPrivateKey(String s) {
773-
return null;
774-
}
775-
}
776-
777-
/**
778-
* This is an empty trust manager that is used in case a loaded trust manager is null
779-
*/
780-
static final class EmptyX509TrustManager extends X509ExtendedTrustManager {
781-
782-
@Override
783-
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
784-
throw new CertificateException("no certificates are trusted");
785-
}
786-
787-
@Override
788-
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
789-
throw new CertificateException("no certificates are trusted");
790-
}
791-
792-
@Override
793-
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
794-
throw new CertificateException("no certificates are trusted");
795-
}
796-
797-
@Override
798-
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
799-
throw new CertificateException("no certificates are trusted");
800-
}
801582

802-
@Override
803-
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
804-
throw new CertificateException("no certificates are trusted");
583+
synchronized void reload() {
584+
invalidateSessions(context.getClientSessionContext());
585+
invalidateSessions(context.getServerSessionContext());
586+
reloadSslContext();
587+
}
588+
589+
private void reloadSslContext() {
590+
try {
591+
X509ExtendedKeyManager loadedKeyManager = Optional.ofNullable(keyConfig.createKeyManager(env)).
592+
orElse(getEmptyKeyManager());
593+
X509ExtendedTrustManager loadedTrustManager = Optional.ofNullable(trustConfig.createTrustManager(env)).
594+
orElse(getEmptyTrustManager());
595+
SSLContext loadedSslContext = SSLContext.getInstance(sslContextAlgorithm(sslConfiguration.supportedProtocols()));
596+
loadedSslContext.init(new X509ExtendedKeyManager[]{loadedKeyManager},
597+
new X509ExtendedTrustManager[]{loadedTrustManager}, null);
598+
supportedCiphers(loadedSslContext.getSupportedSSLParameters().getCipherSuites(), sslConfiguration.cipherSuites(), false);
599+
this.context = loadedSslContext;
600+
} catch (GeneralSecurityException | IOException e) {
601+
throw new ElasticsearchException("failed to initialize the SSLContext", e);
602+
}
805603
}
806-
807-
@Override
808-
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
809-
throw new CertificateException("no certificates are trusted");
604+
X509ExtendedKeyManager getEmptyKeyManager() throws GeneralSecurityException, IOException {
605+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
606+
keyStore.load(null, null);
607+
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
608+
keyManagerFactory.init(keyStore, null);
609+
return (X509ExtendedKeyManager) keyManagerFactory.getKeyManagers()[0];
810610
}
811611

812-
@Override
813-
public X509Certificate[] getAcceptedIssuers() {
814-
return new X509Certificate[0];
612+
X509ExtendedTrustManager getEmptyTrustManager() throws GeneralSecurityException, IOException {
613+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
614+
keyStore.load(null, null);
615+
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
616+
trustManagerFactory.init(keyStore);
617+
return (X509ExtendedTrustManager) trustManagerFactory.getTrustManagers()[0];
815618
}
816619
}
817620

0 commit comments

Comments
 (0)