[SSL/TLS] filter out unsupported ciphers before setting the cipher list

This change filters out unsupported ciphers before setting the ciphers on the
SSLEngine. The unsupported ciphers are logged in a message at the error
level. If none of the specified ciphers are supported, then an exception will
be thrown.

Closes elastic/elasticsearch#698

Original commit: elastic/x-pack-elasticsearch@68cf47ec19
This commit is contained in:
jaymode 2015-02-10 10:39:25 -05:00
parent 6a6e44545c
commit 7cfdf521c3
3 changed files with 92 additions and 1 deletions

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.shield.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.cache.CacheLoader;
import org.elasticsearch.common.cache.LoadingCache;
@ -16,11 +17,15 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.UncheckedExecutionException;
import org.elasticsearch.shield.ShieldSettingsException;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* This service houses the private key and trust managers needed for SSL/TLS negotiation. It is the central place to
@ -92,7 +97,9 @@ public abstract class AbstractSSLService extends AbstractComponent {
SSLEngine createSSLEngine(SSLContext sslContext, String[] ciphers, String[] supportedProtocols, String host, int port) {
SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
try {
sslEngine.setEnabledCipherSuites(ciphers);
sslEngine.setEnabledCipherSuites(supportedCiphers(sslEngine.getSupportedCipherSuites(), ciphers));
} catch (ElasticsearchException e) {
throw e;
} catch (Throwable t) {
throw new ElasticsearchSSLException("failed loading cipher suites [" + Arrays.asList(ciphers) + "]", t);
}
@ -105,6 +112,38 @@ public abstract class AbstractSSLService extends AbstractComponent {
return sslEngine;
}
String[] supportedCiphers(String[] supportedCiphers, String[] requestedCiphers) {
List<String> requestedCiphersList = new ArrayList<>(requestedCiphers.length);
List<String> unsupportedCiphers = new LinkedList<>();
boolean found;
for (String requestedCipher : requestedCiphers) {
found = false;
for (String supportedCipher : supportedCiphers) {
if (supportedCipher.equals(requestedCipher)) {
found = true;
requestedCiphersList.add(requestedCipher);
break;
}
}
if (!found) {
unsupportedCiphers.add(requestedCipher);
}
}
if (requestedCiphersList.isEmpty()) {
throw new ShieldSettingsException("none of the ciphers [" + Arrays.asList(requestedCiphers) + "] are supported by this JVM");
}
if (!unsupportedCiphers.isEmpty()) {
logger.error("unsupported ciphers [{}] were requested but cannot be used in this JVM. If you are trying to use ciphers\n" +
"with a key length greater than 128 bits on an Oracle JVM, you will need to install the unlimited strength\n" +
"JCE policy files. Additionally, please ensure the PKCS11 provider is enabled for your JVM.", unsupportedCiphers);
}
return requestedCiphersList.toArray(new String[requestedCiphersList.size()]);
}
private class SSLContextCacheLoader extends CacheLoader<SSLSettings, SSLContext> {
@Override

View File

@ -22,7 +22,9 @@ import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSessionContext;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.Matchers.*;
@ -207,4 +209,26 @@ public class ClientSSLServiceTests extends ElasticsearchTestCase {
.build());
sslService.sslContext();
}
@Test
public void validCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(Arrays.asList(AbstractSSLService.DEFAULT_CIPHERS));
ciphers.add("foo");
ciphers.add("bar");
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
.putArray("shield.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build());
SSLEngine engine = sslService.createSSLEngine();
assertThat(engine, is(notNullValue()));
String[] enabledCiphers = engine.getEnabledCipherSuites();
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
}
@Test(expected = ShieldSettingsException.class)
public void invalidCiphersOnlyThrowsException() throws Exception {
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
.putArray("shield.ssl.ciphers", new String[] { "foo", "bar" })
.build());
sslService.createSSLEngine();
}
}

View File

@ -17,7 +17,9 @@ import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.Matchers.*;
@ -161,4 +163,30 @@ public class ServerSSLServiceTests extends ElasticsearchTestCase {
.build());
sslService.sslContext();
}
@Test
public void validCiphersAndInvalidCiphersWork() throws Exception {
List<String> ciphers = new ArrayList<>(Arrays.asList(AbstractSSLService.DEFAULT_CIPHERS));
ciphers.add("foo");
ciphers.add("bar");
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
.put("shield.ssl.keystore.path", testnodeStore)
.put("shield.ssl.keystore.password", "testnode")
.putArray("shield.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
.build());
SSLEngine engine = sslService.createSSLEngine();
assertThat(engine, is(notNullValue()));
String[] enabledCiphers = engine.getEnabledCipherSuites();
assertThat(Arrays.asList(enabledCiphers), not(contains("foo", "bar")));
}
@Test(expected = ShieldSettingsException.class)
public void invalidCiphersOnlyThrowsException() throws Exception {
ServerSSLService sslService = new ServerSSLService(settingsBuilder()
.put("shield.ssl.keystore.path", testnodeStore)
.put("shield.ssl.keystore.password", "testnode")
.putArray("shield.ssl.ciphers", new String[] { "foo", "bar" })
.build());
sslService.createSSLEngine();
}
}