diff --git a/solr/core/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java b/solr/core/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java
index f7779510531..60f383ce6ee 100644
--- a/solr/core/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java
+++ b/solr/core/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java
@@ -77,38 +77,63 @@ public class SSLConfig {
/**
* Returns an SslContextFactory that should be used by a jetty server based on the specified
- * configuration, or null if no SSL should be used.
+ * SSLConfig param which may be null.
*
- * The specified sslConfig will be completely ignored if the "tests.jettySsl" system property is
- * true - in which case standard "javax.net.ssl.*" system properties will be used instead, along
- * with "tests.jettySsl.clientAuth"
+ * if the SSLConfig param is non-null, then this method will return the results of
+ * {@link #createContextFactory()}.
*
- * @see #isSSLMode
+ * If the SSLConfig param is null, then this method will return null unless the
+ * tests.jettySsl
system property is true, in which case standard "javax.net.ssl.*"
+ * system properties will be used instead, along with "tests.jettySsl.clientAuth".
+ *
+ * @see #createContextFactory()
*/
public static SslContextFactory createContextFactory(SSLConfig sslConfig) {
- if (sslConfig == null) {
- if (Boolean.getBoolean("tests.jettySsl")) {
- return configureSslFromSysProps();
- }
+ if (sslConfig != null) {
+ return sslConfig.createContextFactory();
+ }
+ // else...
+ if (Boolean.getBoolean("tests.jettySsl")) {
+ return configureSslFromSysProps();
+ }
+ // else...
+ return null;
+ }
+
+ /**
+ * Returns an SslContextFactory that should be used by a jetty server based on this SSLConfig instance,
+ * or null if SSL should not be used.
+ *
+ * The default implementation generates a simple factory according to the keystore, truststore,
+ * and clientAuth properties of this object.
+ *
+ * @see #getKeyStore
+ * @see #getKeyStorePassword
+ * @see #isClientAuthMode
+ * @see #getTrustStore
+ * @see #getTrustStorePassword
+ */
+ public SslContextFactory createContextFactory() {
+
+ if (! isSSLMode()) {
return null;
}
-
- if (!sslConfig.isSSLMode())
- return null;
-
- SslContextFactory factory = new SslContextFactory(false);
- if (sslConfig.getKeyStore() != null)
- factory.setKeyStorePath(sslConfig.getKeyStore());
- if (sslConfig.getKeyStorePassword() != null)
- factory.setKeyStorePassword(sslConfig.getKeyStorePassword());
- factory.setNeedClientAuth(sslConfig.isClientAuthMode());
+ // else...
- if (sslConfig.isClientAuthMode()) {
- if (sslConfig.getTrustStore() != null)
- factory.setTrustStorePath(sslConfig.getTrustStore());
- if (sslConfig.getTrustStorePassword() != null)
- factory.setTrustStorePassword(sslConfig.getTrustStorePassword());
+ SslContextFactory factory = new SslContextFactory(false);
+ if (getKeyStore() != null)
+ factory.setKeyStorePath(getKeyStore());
+ if (getKeyStorePassword() != null)
+ factory.setKeyStorePassword(getKeyStorePassword());
+
+ factory.setNeedClientAuth(isClientAuthMode());
+
+ if (isClientAuthMode()) {
+ if (getTrustStore() != null)
+ factory.setTrustStorePath(getTrustStore());
+ if (getTrustStorePassword() != null)
+ factory.setTrustStorePassword(getTrustStorePassword());
}
return factory;
diff --git a/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java b/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java
index 90496186574..9d65a88223c 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java
@@ -21,6 +21,8 @@ import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SecureRandomSpi;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.SSLContext;
@@ -40,8 +42,10 @@ import org.apache.solr.client.solrj.embedded.SSLConfig;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.HttpClientUtil.SchemaRegistryProvider;
import org.apache.solr.client.solrj.impl.SolrHttpClientBuilder;
+
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.security.CertificateUtils;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
public class SSLTestConfig extends SSLConfig {
public static File TEST_KEYSTORE = ExternalPaths.SERVER_HOME == null ? null
@@ -82,7 +86,10 @@ public class SSLTestConfig extends SSLConfig {
/**
* Builds a new SSLContext for HTTP clients to use when communicating with servers which have
- * been configured based on the settings of this object. Also explicitly allows the use of self-signed
+ * been configured based on the settings of this object.
+ *
+ * NOTE: Uses a completely insecure {@link SecureRandom} instance to prevent tests from blocking
+ * due to lack of entropy, also explicitly allows the use of self-signed
* certificates (since that's what is almost always used during testing).
*/
public SSLContext buildClientSSLContext() throws KeyManagementException,
@@ -91,7 +98,8 @@ public class SSLTestConfig extends SSLConfig {
assert isSSLMode();
SSLContextBuilder builder = SSLContexts.custom();
-
+ builder.setSecureRandom(NullSecureRandom.INSTANCE);
+
// NOTE: KeyStore & TrustStore are swapped because they are from configured from server perspective...
// we are a client - our keystore contains the keys the server trusts, and vice versa
builder.loadTrustMaterial(buildKeyStore(getKeyStore(), getKeyStorePassword()), new TrustSelfSignedStrategy()).build();
@@ -104,6 +112,54 @@ public class SSLTestConfig extends SSLConfig {
return builder.build();
}
+ /**
+ * Builds a new SSLContext for jetty servers which have been configured based on the settings of
+ * this object.
+ *
+ * NOTE: Uses a completely insecure {@link SecureRandom} instance to prevent tests from blocking
+ * due to lack of entropy, also explicitly allows the use of self-signed
+ * certificates (since that's what is almost always used during testing).
+ * almost always used during testing).
+ */
+ public SSLContext buildServerSSLContext() throws KeyManagementException,
+ UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
+
+ assert isSSLMode();
+
+ SSLContextBuilder builder = SSLContexts.custom();
+ builder.setSecureRandom(NullSecureRandom.INSTANCE);
+
+ builder.loadKeyMaterial(buildKeyStore(getKeyStore(), getKeyStorePassword()), getKeyStorePassword().toCharArray());
+
+ if (isClientAuthMode()) {
+ builder.loadTrustMaterial(buildKeyStore(getTrustStore(), getTrustStorePassword()), new TrustSelfSignedStrategy()).build();
+
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns an SslContextFactory using {@link buildServerSSLContext} if SSL should be used, else returns null.
+ */
+ @Override
+ public SslContextFactory createContextFactory() {
+ if (!isSSLMode()) {
+ return null;
+ }
+ // else...
+
+
+ SslContextFactory factory = new SslContextFactory(false);
+ try {
+ factory.setSslContext(buildServerSSLContext());
+ } catch (Exception e) {
+ throw new RuntimeException("ssl context init failure: " + e.getMessage(), e);
+ }
+ factory.setNeedClientAuth(isClientAuthMode());
+ return factory;
+ }
+
/**
* Constructs a KeyStore using the specified filename and password
*/
@@ -202,5 +258,42 @@ public class SSLTestConfig extends SSLConfig {
System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStorePassword");
}
-
+
+ /**
+ * A mocked up instance of SecureRandom that always does the minimal amount of work to generate
+ * "random" numbers. This is to prevent blocking issues that arise in platform default
+ * SecureRandom instances due to too many instances / not enough random entropy.
+ * Tests do not need secure SSL.
+ */
+ private static class NullSecureRandom extends SecureRandom {
+ public static final SecureRandom INSTANCE = new NullSecureRandom();
+
+ /** SPI Used to init all instances */
+ private static final SecureRandomSpi NULL_SPI = new SecureRandomSpi() {
+ /** NOOP: returns new uninitialized byte[] */
+ public byte[] engineGenerateSeed(int numBytes) {
+ return new byte[numBytes];
+ }
+ /** NOOP */
+ public void engineNextBytes(byte[] bytes) { /* NOOP */ }
+ /** NOOP */
+ public void engineSetSeed(byte[] seed) { /* NOOP */ }
+ };
+
+ private NullSecureRandom() {
+ super(NULL_SPI, null) ;
+ }
+
+ /** NOOP: returns new uninitialized byte[] */
+ public byte[] generateSeed(int numBytes) {
+ return new byte[numBytes];
+ }
+ /** NOOP */
+ synchronized public void nextBytes(byte[] bytes) { /* NOOP */ }
+ /** NOOP */
+ synchronized public void setSeed(byte[] seed) { /* NOOP */ }
+ /** NOOP */
+ synchronized public void setSeed(long seed) { /* NOOP */ }
+
+ }
}