Issue #1108 - adding SslContextFactory.dump() selection details
+ Protocol selection details + Cipher Suites selection details Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> Conflicts: jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
This commit is contained in:
parent
9ba7b713d2
commit
5e0d11cfc3
|
@ -35,6 +35,7 @@ import java.security.cert.CollectionCertStoreParameters;
|
|||
import java.security.cert.PKIXBuilderParameters;
|
||||
import java.security.cert.X509CertSelector;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -64,6 +65,8 @@ import javax.net.ssl.X509TrustManager;
|
|||
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -78,7 +81,7 @@ import org.eclipse.jetty.util.security.Password;
|
|||
* creates SSL context based on these parameters to be
|
||||
* used by the SSL connectors.
|
||||
*/
|
||||
public class SslContextFactory extends AbstractLifeCycle
|
||||
public class SslContextFactory extends AbstractLifeCycle implements Dumpable
|
||||
{
|
||||
public final static TrustManager[] TRUST_ALL_CERTS = new X509TrustManager[]{new X509TrustManager()
|
||||
{
|
||||
|
@ -314,7 +317,39 @@ public class SslContextFactory extends AbstractLifeCycle
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
out.append(String.valueOf(this)).append(" trustAll=").append(Boolean.toString(_trustAll)).append(System.lineSeparator());
|
||||
|
||||
SSLEngine sslEngine = newSSLEngine();
|
||||
|
||||
List<Object> selections = new ArrayList<>();
|
||||
|
||||
// protocols
|
||||
selections.add(new SslSelectionDump("Protocol",
|
||||
sslEngine.getSupportedProtocols(),
|
||||
sslEngine.getEnabledProtocols(),
|
||||
getExcludeProtocols(),
|
||||
getIncludeProtocols()));
|
||||
|
||||
// ciphers
|
||||
selections.add(new SslSelectionDump("Cipher Suite",
|
||||
sslEngine.getSupportedCipherSuites(),
|
||||
sslEngine.getEnabledCipherSuites(),
|
||||
getExcludeCipherSuites(),
|
||||
getIncludeCipherSuites()));
|
||||
|
||||
ContainerLifeCycle.dump(out, indent, selections);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
package org.eclipse.jetty.util.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
|
||||
public class SslSelectionDump extends ContainerLifeCycle implements Dumpable
|
||||
{
|
||||
private static class CaptionedList extends ArrayList<String> implements Dumpable
|
||||
{
|
||||
private final String caption;
|
||||
|
||||
public CaptionedList(String caption)
|
||||
{
|
||||
this.caption = caption;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
return ContainerLifeCycle.dump(SslSelectionDump.CaptionedList.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
out.append(caption);
|
||||
out.append(" (size=").append(Integer.toString(size())).append(")");
|
||||
out.append(System.lineSeparator());
|
||||
ContainerLifeCycle.dump(out, indent, this);
|
||||
}
|
||||
}
|
||||
|
||||
private final String type;
|
||||
private SslSelectionDump.CaptionedList enabled = new SslSelectionDump.CaptionedList("Enabled");
|
||||
private SslSelectionDump.CaptionedList disabled = new SslSelectionDump.CaptionedList("Disabled");
|
||||
|
||||
public SslSelectionDump(String type,
|
||||
String[] supportedByJVM,
|
||||
String[] enabledByJVM,
|
||||
String[] excludedByConfig,
|
||||
String[] includedByConfig)
|
||||
{
|
||||
this.type = type;
|
||||
addBean(enabled);
|
||||
addBean(disabled);
|
||||
|
||||
List<String> jvmEnabled = Arrays.asList(enabledByJVM);
|
||||
|
||||
List<Pattern> excludedPatterns = toPatternList(excludedByConfig);
|
||||
List<Pattern> includedPatterns = toPatternList(includedByConfig);
|
||||
|
||||
for(String entry: toSortedList(supportedByJVM))
|
||||
{
|
||||
boolean isPresent = true;
|
||||
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(entry);
|
||||
if (!jvmEnabled.contains(entry))
|
||||
{
|
||||
if (isPresent)
|
||||
{
|
||||
s.append(" -");
|
||||
isPresent = false;
|
||||
}
|
||||
s.append(" JreDisabled:java.security");
|
||||
}
|
||||
|
||||
for (Pattern pattern : excludedPatterns)
|
||||
{
|
||||
Matcher m = pattern.matcher(entry);
|
||||
if (m.matches())
|
||||
{
|
||||
if (isPresent)
|
||||
{
|
||||
s.append(" -");
|
||||
isPresent = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
s.append(",");
|
||||
}
|
||||
s.append(" ConfigExcluded:'").append(pattern.pattern()).append('\'');
|
||||
}
|
||||
}
|
||||
|
||||
if (!includedPatterns.isEmpty())
|
||||
{
|
||||
boolean isIncluded = false;
|
||||
for (Pattern pattern : includedPatterns)
|
||||
{
|
||||
Matcher m = pattern.matcher(entry);
|
||||
if (m.matches())
|
||||
{
|
||||
isIncluded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isIncluded)
|
||||
{
|
||||
if (isPresent)
|
||||
{
|
||||
s.append(" -");
|
||||
isPresent = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
s.append(",");
|
||||
}
|
||||
s.append(" ConfigIncluded:NotSpecified");
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent)
|
||||
{
|
||||
enabled.add(s.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
disabled.add(s.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> toSortedList(String[] strings)
|
||||
{
|
||||
List<String> sorted = new ArrayList<>();
|
||||
for (String entry : strings)
|
||||
{
|
||||
sorted.add(entry);
|
||||
}
|
||||
|
||||
Collections.sort(sorted, new Comparator<String>()
|
||||
{
|
||||
@Override
|
||||
public int compare(String o1, String o2)
|
||||
{
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
});
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
private static List<Pattern> toPatternList(String[] configs)
|
||||
{
|
||||
// Arrays.stream(configs)
|
||||
// .map((entry) -> Pattern.compile(entry))
|
||||
// .collect(Collectors.toList());
|
||||
List<Pattern> ret = new ArrayList<>();
|
||||
for (String entry : configs)
|
||||
{
|
||||
ret.add(Pattern.compile(entry));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
dumpBeans(out, indent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dumpThis(Appendable out) throws IOException
|
||||
{
|
||||
out.append(type).append(" Selections").append(System.lineSeparator());
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.util.ssl;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -32,19 +33,20 @@ import java.security.KeyStore;
|
|||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.JDK;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
|
||||
public class SslContextFactoryTest
|
||||
{
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
private SslContextFactory cf;
|
||||
|
||||
|
@ -54,10 +56,20 @@ public class SslContextFactoryTest
|
|||
cf = new SslContextFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSLOTH() throws Exception
|
||||
{
|
||||
cf.setKeyStorePassword("storepwd");
|
||||
cf.setKeyManagerPassword("keypwd");
|
||||
|
||||
cf.start();
|
||||
|
||||
cf.dump(System.out, "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoTsFileKs() throws Exception
|
||||
{
|
||||
String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore";
|
||||
cf.setKeyStorePassword("storepwd");
|
||||
cf.setKeyManagerPassword("keypwd");
|
||||
|
||||
|
@ -104,7 +116,7 @@ public class SslContextFactoryTest
|
|||
cf.setKeyStoreResource(keystoreResource);
|
||||
cf.setKeyStorePassword("storepwd");
|
||||
cf.setKeyManagerPassword("keypwd");
|
||||
|
||||
|
||||
cf.start();
|
||||
|
||||
assertTrue(cf.getSslContext()!=null);
|
||||
|
@ -130,7 +142,6 @@ public class SslContextFactoryTest
|
|||
@Test
|
||||
public void testResourceTsResourceKsWrongPW() throws Exception
|
||||
{
|
||||
SslContextFactory.LOG.info("EXPECT SslContextFactory@????????(null,null): java.security.UnrecoverableKeyException: Cannot recover key...");
|
||||
Resource keystoreResource = Resource.newSystemResource("keystore");
|
||||
Resource truststoreResource = Resource.newSystemResource("keystore");
|
||||
|
||||
|
@ -140,21 +151,16 @@ public class SslContextFactoryTest
|
|||
cf.setKeyManagerPassword("wrong_keypwd");
|
||||
cf.setTrustStorePassword("storepwd");
|
||||
|
||||
try
|
||||
try (StacklessLogging ignored = new StacklessLogging(AbstractLifeCycle.class))
|
||||
{
|
||||
((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
|
||||
expectedException.expect(java.security.UnrecoverableKeyException.class);
|
||||
cf.start();
|
||||
Assert.fail();
|
||||
}
|
||||
catch(java.security.UnrecoverableKeyException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceTsWrongPWResourceKs() throws Exception
|
||||
{
|
||||
SslContextFactory.LOG.info("EXPECT SslContextFactory@????????(null,null): java.io.IOException: Keystore was tampered with ...");
|
||||
Resource keystoreResource = Resource.newSystemResource("keystore");
|
||||
Resource truststoreResource = Resource.newSystemResource("keystore");
|
||||
|
||||
|
@ -164,35 +170,23 @@ public class SslContextFactoryTest
|
|||
cf.setKeyManagerPassword("keypwd");
|
||||
cf.setTrustStorePassword("wrong_storepwd");
|
||||
|
||||
try
|
||||
try (StacklessLogging ignored = new StacklessLogging(AbstractLifeCycle.class))
|
||||
{
|
||||
((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
|
||||
expectedException.expect(IOException.class);
|
||||
expectedException.expectMessage(containsString("Keystore was tampered with, or password was incorrect"));
|
||||
cf.start();
|
||||
Assert.fail();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoKeyConfig() throws Exception
|
||||
{
|
||||
try
|
||||
try (StacklessLogging ignored = new StacklessLogging(AbstractLifeCycle.class))
|
||||
{
|
||||
SslContextFactory.LOG.info("EXPECT SslContextFactory@????????(null,/foo): java.lang.IllegalStateException: SSL doesn't have a valid keystore...");
|
||||
((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
|
||||
cf.setTrustStorePath("/foo");
|
||||
expectedException.expect(IllegalStateException.class);
|
||||
expectedException.expectMessage(containsString("SSL doesn't have a valid keystore"));
|
||||
cf.start();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.fail("Unexpected exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,40 +205,15 @@ public class SslContextFactoryTest
|
|||
@Test
|
||||
public void testSetIncludeCipherSuitesRegex() throws Exception
|
||||
{
|
||||
// Test does not work on JDK 8+ (RC4 is disabled)
|
||||
cf.setIncludeCipherSuites(".*ECDHE.*",".*WIBBLE.*");
|
||||
Assume.assumeFalse(JDK.IS_8);
|
||||
|
||||
cf.setIncludeCipherSuites(".*RC4.*");
|
||||
cf.start();
|
||||
SSLEngine sslEngine = cf.newSSLEngine();
|
||||
String[] enabledCipherSuites = sslEngine.getEnabledCipherSuites();
|
||||
assertThat("At least 1 cipherSuite is enabled", enabledCipherSuites.length, greaterThan(0));
|
||||
assertThat("At least 1 cipherSuite is enabled", enabledCipherSuites.length, greaterThan(1));
|
||||
for (String enabledCipherSuite : enabledCipherSuites)
|
||||
assertThat("CipherSuite contains RC4", enabledCipherSuite.contains("RC4"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetIncludeCipherSuitesPreservesOrder()
|
||||
{
|
||||
String[] supportedCipherSuites = new String[]{"cipher4", "cipher2", "cipher1", "cipher3"};
|
||||
String[] includeCipherSuites = {"cipher1", "cipher3", "cipher4"};
|
||||
|
||||
cf.setIncludeCipherSuites(includeCipherSuites);
|
||||
String[] selectedCipherSuites = cf.selectCipherSuites(null, supportedCipherSuites);
|
||||
|
||||
assertSelectedMatchesIncluded(includeCipherSuites, selectedCipherSuites);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetIncludeProtocolsPreservesOrder()
|
||||
{
|
||||
String[] supportedProtocol = new String[]{"cipher4", "cipher2", "cipher1", "cipher3"};
|
||||
String[] includeProtocol = {"cipher1", "cipher3", "cipher4"};
|
||||
|
||||
cf.setIncludeProtocols(includeProtocol);
|
||||
String[] selectedProtocol = cf.selectProtocols(null, supportedProtocol);
|
||||
|
||||
assertSelectedMatchesIncluded(includeProtocol, selectedProtocol);
|
||||
assertThat("CipherSuite contains ECDHE", enabledCipherSuite.contains("ECDHE"), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -255,12 +224,4 @@ public class SslContextFactoryTest
|
|||
assertNotNull(cf.getExcludeCipherSuites());
|
||||
assertNotNull(cf.getIncludeCipherSuites());
|
||||
}
|
||||
|
||||
private void assertSelectedMatchesIncluded(String[] includeStrings, String[] selectedStrings)
|
||||
{
|
||||
assertThat(includeStrings.length + " strings are selected", selectedStrings.length, is(includeStrings.length));
|
||||
assertThat("order from includeStrings is preserved", selectedStrings[0], equalTo(includeStrings[0]));
|
||||
assertThat("order from includeStrings is preserved", selectedStrings[1], equalTo(includeStrings[1]));
|
||||
assertThat("order from includeStrings is preserved", selectedStrings[2], equalTo(includeStrings[2]));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue