From ddc9f4446008394ffbfd4ad91e2c531c019c93ea Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 30 Dec 2014 19:20:43 +0900 Subject: [PATCH] Raise an exception when the specified cipher suite is not available Motivation: SSL_set_cipher_list() in OpenSSL does not fail as long as at least one cipher suite is available. It is different from the semantics of SSLEngine.setEnabledCipherSuites(), which raises an exception when the list contains an unavailable cipher suite. Modifications: - Add OpenSsl.isCipherSuiteAvailable(String) which checks the availability of a cipher suite - Raise an IllegalArgumentException when the specified cipher suite is not available Result: Fixed compatibility --- .../java/io/netty/handler/ssl/OpenSsl.java | 28 +++++++++++----- .../io/netty/handler/ssl/OpenSslEngine.java | 33 ++++++++++++++++--- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index d87bd2dfb0..9ccaa29178 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -24,9 +24,9 @@ import org.apache.tomcat.jni.Pool; import org.apache.tomcat.jni.SSL; import org.apache.tomcat.jni.SSLContext; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.LinkedHashSet; +import java.util.Set; /** * Tells if {@code netty-tcnative} and its OpenSSL support @@ -37,7 +37,7 @@ public final class OpenSsl { private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class); private static final Throwable UNAVAILABILITY_CAUSE; - private static final List AVAILABLE_CIPHER_SUITES; + private static final Set AVAILABLE_CIPHER_SUITES; static { Throwable cause = null; @@ -54,7 +54,7 @@ public final class OpenSsl { UNAVAILABILITY_CAUSE = cause; if (cause == null) { - final List availableCipherSuites = new ArrayList(128); + final Set availableCipherSuites = new LinkedHashSet(128); final long aprPool = Pool.create(0); try { final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); @@ -82,9 +82,9 @@ public final class OpenSsl { Pool.destroy(aprPool); } - AVAILABLE_CIPHER_SUITES = Collections.unmodifiableList(availableCipherSuites); + AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites); } else { - AVAILABLE_CIPHER_SUITES = Collections.emptyList(); + AVAILABLE_CIPHER_SUITES = Collections.emptySet(); } } @@ -124,8 +124,20 @@ public final class OpenSsl { * Returns all the available OpenSSL cipher suites. * Please note that the returned array may include the cipher suites that are insecure or non-functional. */ - public static String[] availableCipherSuites() { - return AVAILABLE_CIPHER_SUITES.toArray(new String[AVAILABLE_CIPHER_SUITES.size()]); + public static Set availableCipherSuites() { + return AVAILABLE_CIPHER_SUITES; + } + + /** + * Returns {@code true} if and only if the specified cipher suite is available in OpenSSL. + * Both Java-style cipher suite and OpenSSL-style cipher suite are accepted. + */ + public static boolean isCipherSuiteAvailable(String cipherSuite) { + String converted = CipherSuiteConverter.toOpenSsl(cipherSuite); + if (converted != null) { + cipherSuite = converted; + } + return AVAILABLE_CIPHER_SUITES.contains(cipherSuite); } static boolean isError(long errorCode) { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java index fccd9cc38f..196d52a431 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java @@ -650,7 +650,8 @@ public final class OpenSslEngine extends SSLEngine { @Override public String[] getSupportedCipherSuites() { - return OpenSsl.availableCipherSuites(); + Set availableCipherSuites = OpenSsl.availableCipherSuites(); + return availableCipherSuites.toArray(new String[availableCipherSuites.size()]); } @Override @@ -677,11 +678,35 @@ public final class OpenSslEngine extends SSLEngine { throw new NullPointerException("cipherSuites"); } - final String converted = CipherSuiteConverter.toOpenSsl(Arrays.asList(cipherSuites)); + final StringBuilder buf = new StringBuilder(); + for (String c: cipherSuites) { + if (c == null) { + break; + } + + String converted = CipherSuiteConverter.toOpenSsl(c); + if (converted == null) { + converted = c; + } + + if (!OpenSsl.isCipherSuiteAvailable(converted)) { + throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')'); + } + + buf.append(converted); + buf.append(':'); + } + + if (buf.length() == 0) { + throw new IllegalArgumentException("empty cipher suites"); + } + buf.setLength(buf.length() - 1); + + final String cipherSuiteSpec = buf.toString(); try { - SSL.setCipherSuites(ssl, converted); + SSL.setCipherSuites(ssl, cipherSuiteSpec); } catch (Exception e) { - throw new IllegalStateException("failed to enable cipher suites: " + converted, e); + throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e); } }