Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
Expand Down Expand Up @@ -163,6 +165,15 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
= StrictHostnameVerifier.INSTANCE;

private static final String WEAK_KEY_EXCHANGES
= "^(TLS|SSL)_(NULL|ECDH_anon|DH_anon|DH_anon_EXPORT|DHE_RSA_EXPORT|DHE_DSS_EXPORT|"
+ "DSS_EXPORT|DH_DSS_EXPORT|DH_RSA_EXPORT|RSA_EXPORT|KRB5_EXPORT)_(.*)";
private static final String WEAK_CIPHERS
= "^(TLS|SSL)_(.*)_WITH_(NULL|DES_CBC|DES40_CBC|DES_CBC_40|3DES_EDE_CBC|RC4_128|RC4_40|RC2_CBC_40)_(.*)";
private static final List<Pattern> WEAK_CIPHER_SUITE_PATTERNS = Collections.unmodifiableList(Arrays.asList(
Pattern.compile(WEAK_KEY_EXCHANGES, Pattern.CASE_INSENSITIVE),
Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE)));

private final Log log = LogFactory.getLog(getClass());

/**
Expand All @@ -183,6 +194,15 @@ public static SSLConnectionSocketFactory getSocketFactory() throws SSLInitializa
return new SSLConnectionSocketFactory(SSLContexts.createDefault(), getDefaultHostnameVerifier());
}

static boolean isWeakCipherSuite(final String cipherSuite) {
for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) {
if (pattern.matcher(cipherSuite).matches()) {
return true;
}
}
return false;
}

private static String[] split(final String s) {
if (TextUtils.isBlank(s)) {
return null;
Expand Down Expand Up @@ -392,6 +412,18 @@ public Socket createLayeredSocket(
}
if (supportedCipherSuites != null) {
sslsock.setEnabledCipherSuites(supportedCipherSuites);
} else {
// If cipher suites are not explicitly set, remove all insecure ones
final String[] allCipherSuites = sslsock.getEnabledCipherSuites();
final List<String> enabledCipherSuites = new ArrayList<String>(allCipherSuites.length);
for (final String cipherSuite : allCipherSuites) {
if (!isWeakCipherSuite(cipherSuite)) {
enabledCipherSuites.add(cipherSuite);
}
}
if (!enabledCipherSuites.isEmpty()) {
sslsock.setEnabledCipherSuites(enabledCipherSuites.toArray(new String[enabledCipherSuites.size()]));
}
}

if (this.log.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,79 @@ public void testSSLTimeout() throws Exception {
inputStream.close();
}
}

@Test
public void testStrongCipherSuites() {
final String[] strongCipherSuites = {
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
};
for (final String cipherSuite : strongCipherSuites) {
Assert.assertFalse(SSLConnectionSocketFactory.isWeakCipherSuite(cipherSuite));
}
}

@Test
public void testWeakCiphersDisabledByDefault() {
final String[] weakCiphersSuites = {
"SSL_RSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_DH_anon_WITH_AES_128_CBC_SHA",
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_RSA_WITH_NULL_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_DH_anon_WITH_AES_256_GCM_SHA384",
"TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_NULL_SHA256",
"SSL_RSA_EXPORT_WITH_RC4_40_MD5",
"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
"TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
"SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"
};
for (final String cipherSuite : weakCiphersSuites) {
Assert.assertTrue(SSLConnectionSocketFactory.isWeakCipherSuite(cipherSuite));
try {
testWeakCipherDisabledByDefault(cipherSuite);
Assert.fail("IOException expected");
} catch (final Exception e) {
Assert.assertTrue(e instanceof IOException || e instanceof IllegalArgumentException);
}
}
}

private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Exception {
// @formatter:off
this.server = ServerBootstrap.bootstrap()
.setServerInfo(LocalServerTestBase.ORIGIN)
.setSslContext(SSLTestContexts.createServerSSLContext())
.setSslSetupHandler(new SSLServerSetupHandler() {

@Override
public void initialize(final SSLServerSocket socket) {
socket.setEnabledCipherSuites(new String[] {cipherSuite});
}

})
.create();
// @formatter:on
this.server.start();

final HttpContext context = new BasicHttpContext();
final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
SSLTestContexts.createClientSSLContext());
final Socket socket = socketFactory.createSocket(context);
try {
final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
final HttpHost target = new HttpHost("localhost", this.server.getLocalPort(), "https");
socketFactory.connectSocket(0, socket, target, remoteAddress, null, context);
} finally {
socket.close();
}
}
}