Index: java/org/apache/tomcat/util/net/AbstractEndpoint.java =================================================================== --- java/org/apache/tomcat/util/net/AbstractEndpoint.java (revision 1562738) +++ java/org/apache/tomcat/util/net/AbstractEndpoint.java (working copy) @@ -874,6 +874,10 @@ private String sslProtocol = "TLS"; public String getSslProtocol() { return sslProtocol;} public void setSslProtocol(String s) { sslProtocol = s;} + + private String useCipherSuitesOrder = "false"; + public String getUseCipherSuitesOrder() { return useCipherSuitesOrder;} + public void setUseCipherSuitesOrder(String s) { this.useCipherSuitesOrder = s;} private String ciphers = null; public String getCiphers() { return ciphers;} Index: java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java =================================================================== --- java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java (revision 1562738) +++ java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java (working copy) @@ -52,6 +52,7 @@ import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSession; @@ -147,6 +148,7 @@ protected String[] enabledCiphers; protected String[] enabledProtocols; protected boolean allowUnsafeLegacyRenegotiation = false; + protected boolean orderNotSupportedLogged = false; /** * Flag to state that we require client authentication. @@ -739,8 +741,48 @@ } /** + * Tries to configure SSL server socket to honor cipher suites order. + * Configuration is done using reflection. + */ + protected void configureUseCipherSuitesOrder(SSLServerSocket socket) { + String useCipherSuitesOrderStr = endpoint + .getUseCipherSuitesOrder().trim(); + + if ("true".equalsIgnoreCase(useCipherSuitesOrderStr) + || "yes".equalsIgnoreCase(useCipherSuitesOrderStr)) { + + SSLParameters sslParameters = socket.getSSLParameters(); + java.lang.reflect.Method method; + try { + /* + * Method setUseCipherSuitesOrder is only available since + * Java 8 (OpenJDK 1.8.0 B108+), so we must use the reflection. + */ + method = sslParameters.getClass() + .getMethod("setUseCipherSuitesOrder", Boolean.TYPE); + method.invoke(sslParameters, Boolean.TRUE); + socket.setSSLParameters(sslParameters); + } catch (NoSuchMethodException e) { + if (!orderNotSupportedLogged) { + // Avoid logging the same message several times + log.warn(sm.getString("jsse.order_method_not_supported")); + orderNotSupportedLogged = true; + } + } catch (Exception e) { + /* + * Make other exceptions like IllegalAccessException + * unchecked. + */ + throw new RuntimeException(e); + } + } + + } + + /** * Configures the given SSL server socket with the requested cipher suites, - * protocol versions, and need for client authentication + * protocol versions, need for client authentication, and cipher suites + * order. */ private void initServerSocket(ServerSocket ssocket) { @@ -752,6 +794,8 @@ // we don't know if client auth is needed - // after parsing the request we may re-handshake configureClientAuth(socket); + + configureUseCipherSuitesOrder(socket); } /** Index: java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties =================================================================== --- java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties (revision 1562738) +++ java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties (working copy) @@ -19,6 +19,7 @@ jsse.invalid_truststore_password=The provided trust store password could not be used to unlock and/or validate the trust store. Retrying to access the trust store with a null password which will skip validation. jsse.invalidTrustManagerClassName=The trustManagerClassName provided [{0}] does not implement javax.net.ssl.TrustManager jsse.requested_ciphers_not_supported=None of the ciphers specified are supported by the SSL engine : {0} +jsse.order_method_not_supported=Method setUseCipherSuitesOrder is not supported by the SSL engine jsse.enableable_ciphers=Specified SSL ciphers that are supported and enableable are : {0} jsse.unsupported_ciphers=Some specified SSL ciphers are not supported by the SSL engine : {0} jsse.requested_protocols_not_supported=None of the SSL protocols specified are supported by the SSL engine : {0} Index: java/org/apache/tomcat/util/net/NioEndpoint.java =================================================================== --- java/org/apache/tomcat/util/net/NioEndpoint.java (revision 1562738) +++ java/org/apache/tomcat/util/net/NioEndpoint.java (working copy) @@ -44,6 +44,7 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSessionContext; import javax.net.ssl.X509KeyManager; @@ -152,6 +153,10 @@ new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, socketProperties.getBufferPoolSize()); + /** + * Is "ordering not supported" message already logged? + */ + private boolean orderNotSupportedLogged = false; // ------------------------------------------------------------- Properties @@ -586,12 +591,54 @@ engine.setUseClientMode(false); engine.setEnabledCipherSuites(enabledCiphers); engine.setEnabledProtocols(enabledProtocols); + + configureUseCipherSuitesOrder(engine); handler.onCreateSSLEngine(engine); return engine; } + + /** + * Tries to configure SSL server socket to honor cipher suites order. + * Configuration is done using reflection. + */ + protected void configureUseCipherSuitesOrder(SSLEngine engine) { + String useCipherSuitesOrderStr = this.getUseCipherSuitesOrder().trim(); + + if ("true".equalsIgnoreCase(useCipherSuitesOrderStr) + || "yes".equalsIgnoreCase(useCipherSuitesOrderStr)) { + + SSLParameters sslParameters = engine.getSSLParameters(); + java.lang.reflect.Method method; + try { + /* + * Method setUseCipherSuitesOrder is only available since + * Java 8 (OpenJDK 1.8.0 B108+), so we must use the reflection. + */ + method = sslParameters.getClass() + .getMethod("setUseCipherSuitesOrder", Boolean.TYPE); + method.invoke(sslParameters, Boolean.TRUE); + engine.setSSLParameters(sslParameters); + } catch (NoSuchMethodException e) { + if (!orderNotSupportedLogged) { + // Avoid logging the same message several times + log.warn(sm.getString("endpoint.nio.order_method_not_supported")); + orderNotSupportedLogged = true; + } + } catch (Exception e) { + /* + * Make other exceptions like IllegalAccessException + * unchecked. + */ + throw new RuntimeException(e); + } + } + } + + + /** * Returns true if a worker thread is available for processing. * @return boolean Index: java/org/apache/tomcat/util/net/res/LocalStrings.properties =================================================================== --- java/org/apache/tomcat/util/net/res/LocalStrings.properties (revision 1562738) +++ java/org/apache/tomcat/util/net/res/LocalStrings.properties (working copy) @@ -59,3 +59,4 @@ endpoint.apr.pollUnknownEvent=A socket was returned from the poller with an unrecognized event [{0}] endpoint.apr.remoteport=APR socket [{0}] opened with remote port [{1}] endpoint.nio.selectorCloseFail=Failed to close selector when closing the poller +endpoint.nio.order_method_not_supported=Method setUseCipherSuitesOrder is not supported by the SSL engine Index: webapps/docs/config/http.xml =================================================================== --- webapps/docs/config/http.xml (revision 1562738) +++ webapps/docs/config/http.xml (working copy) @@ -949,6 +949,16 @@

+ +

Set to true to enforce the server's cipher order + (from the ciphers setting) instead of allowing + the client to choose the cipher (which is the default). + Setting this parameter to "true" will only work if the underlaying SSL + engine supports method + javax.net.SSLParameters.setUseCipherSuitesOrder(boolean) + (e.g. OpenJDK 1.8.0 B108+ does).

+
+

The comma separated list of encryption ciphers to support for HTTPS connections. If specified, only the ciphers that are listed and supported