Merge pull request #142 from artem-smotrakov/HTTPCLIENT-1969
HTTPCLIENT-1969: Filter out weak cipher suites
This commit is contained in:
commit
7f5668797a
|
@ -359,4 +359,59 @@ public class TestSSLSocketFactory {
|
|||
socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context);
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
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()
|
||||
.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());
|
||||
try (final Socket socket = socketFactory.createSocket(context)) {
|
||||
final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort());
|
||||
final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort());
|
||||
socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@ import java.net.InetSocketAddress;
|
|||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
|
@ -67,6 +69,15 @@ import org.slf4j.LoggerFactory;
|
|||
@Contract(threading = ThreadingBehavior.STATELESS)
|
||||
public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactory {
|
||||
|
||||
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 Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
|
@ -96,6 +107,15 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
|
|||
HttpsSupport.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 final javax.net.ssl.SSLSocketFactory socketfactory;
|
||||
private final HostnameVerifier hostnameVerifier;
|
||||
private final String[] supportedProtocols;
|
||||
|
@ -232,6 +252,18 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
|
|||
}
|
||||
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<>(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()) {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.hc.client5.http.ssl;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SSLConnectionSocketFactory}.
|
||||
*/
|
||||
public class TestSSLSocketFactory {
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue