Skip to content

Commit

Permalink
Move ciphers/SSLCipherSuite to SSLHostConfig
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1677135 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
markt-asf committed May 1, 2015
1 parent c273262 commit ef303e3
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 81 deletions.
3 changes: 0 additions & 3 deletions java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
Expand Up @@ -41,9 +41,6 @@ public void setKeystoreProvider(String s ) {
public String getSslProtocol() { return getEndpoint().getSslProtocol();} public String getSslProtocol() { return getEndpoint().getSslProtocol();}
public void setSslProtocol(String s) { getEndpoint().setSslProtocol(s);} public void setSslProtocol(String s) { getEndpoint().setSslProtocol(s);}


public String getCiphers() { return getEndpoint().getCiphers();}
public void setCiphers(String s) { getEndpoint().setCiphers(s);}

public String getKeyAlias() { return getEndpoint().getKeyAlias();} public String getKeyAlias() { return getEndpoint().getKeyAlias();}
public void setKeyAlias(String s ) { getEndpoint().setKeyAlias(s);} public void setKeyAlias(String s ) { getEndpoint().setKeyAlias(s);}


Expand Down
10 changes: 10 additions & 0 deletions java/org/apache/coyote/http11/AbstractHttp11Protocol.java
Expand Up @@ -421,6 +421,16 @@ public void setSSLHonorCipherOrder(boolean honorCipherOrder) {
} }




public void setCiphers(String ciphers) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCiphers(ciphers);
}
public void setSSLCipherSuite(String ciphers) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCiphers(ciphers);
}


// ------------------------------------------------------------- Common code // ------------------------------------------------------------- Common code


// Common configuration required for all new HTTP11 processors // Common configuration required for all new HTTP11 processors
Expand Down
7 changes: 0 additions & 7 deletions java/org/apache/coyote/http11/Http11AprProtocol.java
Expand Up @@ -80,13 +80,6 @@ public boolean isAprRequired() {
public void setSSLPassword(String SSLPassword) { ((AprEndpoint)getEndpoint()).setSSLPassword(SSLPassword); } public void setSSLPassword(String SSLPassword) { ((AprEndpoint)getEndpoint()).setSSLPassword(SSLPassword); }




/**
* SSL cipher suite.
*/
public String getSSLCipherSuite() { return ((AprEndpoint)getEndpoint()).getSSLCipherSuite(); }
public void setSSLCipherSuite(String SSLCipherSuite) { ((AprEndpoint)getEndpoint()).setSSLCipherSuite(SSLCipherSuite); }


/** /**
* SSL certificate chain file. * SSL certificate chain file.
*/ */
Expand Down
8 changes: 0 additions & 8 deletions java/org/apache/tomcat/util/net/AbstractEndpoint.java
Expand Up @@ -51,8 +51,6 @@ public abstract class AbstractEndpoint<S> {


// -------------------------------------------------------------- Constants // -------------------------------------------------------------- Constants


protected static final String DEFAULT_CIPHERS = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA";

protected static final StringManager sm = StringManager.getManager( protected static final StringManager sm = StringManager.getManager(
AbstractEndpoint.class.getPackage().getName()); AbstractEndpoint.class.getPackage().getName());


Expand Down Expand Up @@ -1000,12 +998,6 @@ public void setSslImplementationName(String s) {
public String getSslProtocol() { return sslProtocol;} public String getSslProtocol() { return sslProtocol;}
public void setSslProtocol(String s) { sslProtocol = s;} public void setSslProtocol(String s) { sslProtocol = s;}


private String ciphers = DEFAULT_CIPHERS;
public String getCiphers() { return ciphers;}
public void setCiphers(String s) {
ciphers = s;
}

private String keyAlias = null; private String keyAlias = null;
public String getKeyAlias() { return keyAlias;} public String getKeyAlias() { return keyAlias;}
public void setKeyAlias(String s ) { keyAlias = s;} public void setKeyAlias(String s ) { keyAlias = s;}
Expand Down
10 changes: 1 addition & 9 deletions java/org/apache/tomcat/util/net/AprEndpoint.java
Expand Up @@ -217,14 +217,6 @@ protected Type getSslConfigType() {
public void setSSLPassword(String SSLPassword) { this.SSLPassword = SSLPassword; } public void setSSLPassword(String SSLPassword) { this.SSLPassword = SSLPassword; }




/**
* SSL cipher suite.
*/
protected String SSLCipherSuite = DEFAULT_CIPHERS;
public String getSSLCipherSuite() { return SSLCipherSuite; }
public void setSSLCipherSuite(String SSLCipherSuite) { this.SSLCipherSuite = SSLCipherSuite; }


/** /**
* SSL certificate chain file. * SSL certificate chain file.
*/ */
Expand Down Expand Up @@ -571,7 +563,7 @@ public void bind() throws Exception {
} }


// List the ciphers that the client is permitted to negotiate // List the ciphers that the client is permitted to negotiate
SSLContext.setCipherSuite(ctx, SSLCipherSuite); SSLContext.setCipherSuite(ctx, sslHostConfig.getCiphers());
// Load Server key and certificate // Load Server key and certificate
SSLContext.setCertificate(ctx, sslHostConfig.getCertificateFile(), SSLContext.setCertificate(ctx, sslHostConfig.getCertificateFile(),
sslHostConfig.getCertificateKeyFile(), SSLPassword, SSL.SSL_AIDX_RSA); sslHostConfig.getCertificateKeyFile(), SSLPassword, SSL.SSL_AIDX_RSA);
Expand Down
36 changes: 36 additions & 0 deletions java/org/apache/tomcat/util/net/SSLHostConfig.java
Expand Up @@ -25,6 +25,7 @@


import org.apache.juli.logging.Log; import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory; import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.jsse.openssl.OpenSSLCipherConfigurationParser;
import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.res.StringManager;


public class SSLHostConfig { public class SSLHostConfig {
Expand All @@ -46,6 +47,7 @@ public class SSLHostConfig {
// Common // Common
private CertificateVerification certificateVerification = CertificateVerification.NONE; private CertificateVerification certificateVerification = CertificateVerification.NONE;
private int certificateVerificationDepth = 10; private int certificateVerificationDepth = 10;
private String ciphers = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA";
private boolean honorCipherOrder = false; private boolean honorCipherOrder = false;


private Set<String> protocols = new HashSet<>(); private Set<String> protocols = new HashSet<>();
Expand Down Expand Up @@ -123,6 +125,40 @@ public int getCertificateVerificationDepth() {
} }




public void setCiphers(String ciphersList) {
// Ciphers is stored in OpenSSL format. Convert the provided value if
// necessary.
if (ciphersList != null && !ciphersList.contains(":")) {
StringBuilder sb = new StringBuilder();
// Not obviously in OpenSSL format. May be a single OpenSSL or JSSE
// cipher name. May be a comma separated list of cipher names
String ciphers[] = ciphersList.split(",");
for (String cipher : ciphers) {
String trimmed = cipher.trim();
if (trimmed.length() > 0) {
String openSSLName = OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed);
if (openSSLName == null) {
// Not a JSSE name. Maybe an OpenSSL name or alias
openSSLName = trimmed;
}
if (sb.length() > 0) {
sb.append(':');
}
sb.append(openSSLName);
}
}
this.ciphers = sb.toString();
} else {
this.ciphers = ciphersList;
}
}


public String getCiphers() {
return ciphers;
}


public void setHonorCipherOrder(boolean honorCipherOrder) { public void setHonorCipherOrder(boolean honorCipherOrder) {
this.honorCipherOrder = honorCipherOrder; this.honorCipherOrder = honorCipherOrder;
} }
Expand Down
38 changes: 4 additions & 34 deletions java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
Expand Up @@ -88,14 +88,12 @@ public class JSSESocketFactory implements SSLUtil {
= System.getProperty("user.home") + "/.keystore"; = System.getProperty("user.home") + "/.keystore";
private static final int defaultSessionCacheSize = 0; private static final int defaultSessionCacheSize = 0;
private static final int defaultSessionTimeout = 86400; private static final int defaultSessionTimeout = 86400;
private static final String ALLOW_ALL_SUPPORTED_CIPHERS = "ALL";
public static final String DEFAULT_KEY_PASS = "changeit"; public static final String DEFAULT_KEY_PASS = "changeit";


private final AbstractEndpoint<?> endpoint; private final AbstractEndpoint<?> endpoint;
private final SSLHostConfig sslHostConfig; private final SSLHostConfig sslHostConfig;


private final String[] defaultServerProtocols; private final String[] defaultServerProtocols;
private final String[] defaultServerCipherSuites;




public JSSESocketFactory (AbstractEndpoint<?> endpoint, SSLHostConfig sslHostConfig) { public JSSESocketFactory (AbstractEndpoint<?> endpoint, SSLHostConfig sslHostConfig) {
Expand Down Expand Up @@ -127,20 +125,12 @@ public JSSESocketFactory (AbstractEndpoint<?> endpoint, SSLHostConfig sslHostCon
// This is very likely to be fatal but there is a slim chance that // This is very likely to be fatal but there is a slim chance that
// the JSSE implementation just doesn't like creating unbound // the JSSE implementation just doesn't like creating unbound
// sockets so allow the code to proceed. // sockets so allow the code to proceed.
defaultServerCipherSuites = new String[0];
defaultServerProtocols = new String[0]; defaultServerProtocols = new String[0];
log.warn(sm.getString("jsse.noDefaultCiphers", endpoint.getName()));
log.warn(sm.getString("jsse.noDefaultProtocols", endpoint.getName())); log.warn(sm.getString("jsse.noDefaultProtocols", endpoint.getName()));
return; return;
} }


try { try {
defaultServerCipherSuites = socket.getEnabledCipherSuites();
if (defaultServerCipherSuites.length == 0) {
log.warn(sm.getString("jsse.noDefaultCiphers",
endpoint.getName()));
}

// Filter out all the SSL protocols (SSLv2 and SSLv3) from the // Filter out all the SSL protocols (SSLv2 and SSLv3) from the
// defaults // defaults
// since they are no longer considered secure // since they are no longer considered secure
Expand Down Expand Up @@ -171,33 +161,13 @@ public JSSESocketFactory (AbstractEndpoint<?> endpoint, SSLHostConfig sslHostCon


@Override @Override
public String[] getEnableableCiphers(SSLContext context) { public String[] getEnableableCiphers(SSLContext context) {
String requestedCiphersStr = endpoint.getCiphers(); String requestedCiphersStr = sslHostConfig.getCiphers();


if (ALLOW_ALL_SUPPORTED_CIPHERS.equals(requestedCiphersStr)) { List<String> requestedCiphers =
return context.getSupportedSSLParameters().getCipherSuites(); OpenSSLCipherConfigurationParser.parseExpression(requestedCiphersStr);
}
if ((requestedCiphersStr == null)
|| (requestedCiphersStr.trim().length() == 0)) {
return defaultServerCipherSuites;
}


List<String> requestedCiphers = new ArrayList<>();
if (requestedCiphersStr.indexOf(':') != -1) {
requestedCiphers = OpenSSLCipherConfigurationParser.parseExpression(requestedCiphersStr);
} else {
for (String rc : requestedCiphersStr.split(",")) {
final String cipher = rc.trim();
if (cipher.length() > 0) {
requestedCiphers.add(cipher);
}
}
}
if (requestedCiphers.isEmpty()) {
return defaultServerCipherSuites;
}
List<String> ciphers = new ArrayList<>(requestedCiphers); List<String> ciphers = new ArrayList<>(requestedCiphers);
ciphers.retainAll(Arrays.asList(context.getSupportedSSLParameters() ciphers.retainAll(Arrays.asList(context.getSupportedSSLParameters().getCipherSuites()));
.getCipherSuites()));


if (ciphers.isEmpty()) { if (ciphers.isEmpty()) {
log.warn(sm.getString("jsse.requested_ciphers_not_supported", log.warn(sm.getString("jsse.requested_ciphers_not_supported",
Expand Down
Expand Up @@ -21,6 +21,7 @@
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
Expand Down Expand Up @@ -373,6 +374,8 @@ public class OpenSSLCipherConfigurationParser {
private static final String ALL = "ALL"; private static final String ALL = "ALL";
private static final String COMPLEMENTOFALL = "COMPLEMENTOFALL"; private static final String COMPLEMENTOFALL = "COMPLEMENTOFALL";


private static final Map<String,String> jsseToOpenSSL = new HashMap<>();

private static final void init() { private static final void init() {


for (Cipher cipher : Cipher.values()) { for (Cipher cipher : Cipher.values()) {
Expand All @@ -385,6 +388,12 @@ private static final void init() {
aliases.put(alias, list); aliases.put(alias, list);
} }
aliases.put(cipher.name(), Collections.singletonList(cipher)); aliases.put(cipher.name(), Collections.singletonList(cipher));

jsseToOpenSSL.put(cipher.name(), cipher.getOpenSSLAlias());
Set<String> jsseNames = cipher.getJsseNames();
for (String jsseName : jsseNames) {
jsseToOpenSSL.put(jsseName, cipher.getOpenSSLAlias());
}
} }
List<Cipher> allCiphersList = Arrays.asList(Cipher.values()); List<Cipher> allCiphersList = Arrays.asList(Cipher.values());
Collections.reverse(allCiphersList); Collections.reverse(allCiphersList);
Expand Down Expand Up @@ -703,6 +712,13 @@ public static List<String> parseExpression(String expression) {
return convertForJSSE(parse(expression)); return convertForJSSE(parse(expression));
} }


public static String jsseToOpenSSL(String cipher) {
if (!initialized) {
init();
}
return jsseToOpenSSL.get(cipher);
}

static String displayResult(Collection<Cipher> ciphers, boolean useJSSEFormat, String separator) { static String displayResult(Collection<Cipher> ciphers, boolean useJSSEFormat, String separator) {
if (ciphers.isEmpty()) { if (ciphers.isEmpty()) {
return ""; return "";
Expand Down
68 changes: 68 additions & 0 deletions test/org/apache/tomcat/util/net/TestSSLHostConfig.java
@@ -0,0 +1,68 @@
/*
* 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.
*/
package org.apache.tomcat.util.net;

import org.junit.Assert;
import org.junit.Test;

import org.apache.tomcat.util.net.jsse.openssl.Cipher;

public class TestSSLHostConfig {

@Test
public void testCipher01() {
SSLHostConfig hc = new SSLHostConfig();
Cipher c = Cipher.TLS_RSA_WITH_NULL_MD5;

// Single JSSE name
hc.setCiphers(c.getJsseNames().iterator().next());
Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
}


@Test
public void testCipher02() {
SSLHostConfig hc = new SSLHostConfig();
Cipher c1 = Cipher.TLS_RSA_WITH_NULL_MD5;
Cipher c2 = Cipher.TLS_RSA_WITH_NULL_SHA;

// Two JSSE names
hc.setCiphers(c1.getJsseNames().iterator().next() + "," +
c2.getJsseNames().iterator().next());
Assert.assertEquals(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias(), hc.getCiphers());
}


@Test
public void testCipher03() {
SSLHostConfig hc = new SSLHostConfig();
// Single OpenSSL alias
hc.setCiphers("ALL");
Assert.assertEquals("ALL", hc.getCiphers());
}


@Test
public void testCipher04() {
SSLHostConfig hc = new SSLHostConfig();
Cipher c = Cipher.TLS_RSA_WITH_NULL_MD5;

// Single OpenSSLName name
hc.setCiphers(c.getOpenSSLAlias());
Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
}
}
42 changes: 22 additions & 20 deletions webapps/docs/config/http.xml
Expand Up @@ -1085,6 +1085,22 @@
of 10 will be used.</p> of 10 will be used.</p>
</attribute> </attribute>


<attribute name="ciphers" required="false">
<p>The ciphers to enable using the OpenSSL syntax. (See the OpenSSL
documentation for the list of ciphers supported and the syntax).
Alternatively, a comma separated list of ciphers using with the standard
OpenSSL cipher names or the standard JSSE cipher names may be used.</p>
<p>When converting from OpenSSL syntax to JSSE ciphers for JSSE based
connectors, the behaviour of the OpenSSL syntax parsing is kept aligned
with the behaviour of the OpenSSL 1.1.0 development branch.</p>
<p>Only the ciphers that are supported by the SSL implementation will be
used.</p>
<p>If not specified, a default (using the OpenSSL notation) of
<code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5</code> will be used.</p>
<p>Note that, by default, the order in which ciphers are defined is not
trreated as an order of preference. See <code>honorCipherOrder</code>.</p>
</attribute>

<attribute name="honorCipherOrder" required="false"> <attribute name="honorCipherOrder" required="false">
<p>Set to <code>true</code> to enforce the server's cipher order <p>Set to <code>true</code> to enforce the server's cipher order
(from the <code>ciphers</code> setting) instead of allowing (from the <code>ciphers</code> setting) instead of allowing
Expand Down Expand Up @@ -1145,27 +1161,14 @@
<attributes> <attributes>


<attribute name="algorithm" required="false"> <attribute name="algorithm" required="false">
<p>This is an alias for the <code>keyManagerAlgorithm</code> attribute of the <p>This is an alias for the <code>keyManagerAlgorithm</code> attribute of
default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> the default <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a>
element.</p> element.</p>
</attribute> </attribute>


<attribute name="ciphers" required="false"> <attribute name="ciphers" required="false">
<p>If specified and using ',' as a separator, only the ciphers that are <p>This is an alias for the <code>ciphers</code> attribute of the default
listed and supported by the SSL implementation will be used. <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
The ciphers are specified using the JSSE cipher naming convention. The
special value of <code>ALL</code> will enable all supported ciphers. This
will include many that are not secure. <code>ALL</code> is intended for
testing purposes only.</p>
<p>The list can also use ':' as a separator, in that case
it will use the OpenSSL syntax (see OpenSSL documentation for the list
of ciphers supported and the syntax). The behaviour of this filtering is
kept aligned with the behaviour of the OpenSSL 1.1.0 development
branch.</p>
<p>If not specified, a default (using the OpenSSL notation) of
<code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5</code> will be used.</p>
<p>Note that Java does not treat the order in which ciphers are defined as
an order of preference. See <code>useServerCipherSuitesOrder</code>.</p>
</attribute> </attribute>


<attribute name="clientAuth" required="false"> <attribute name="clientAuth" required="false">
Expand Down Expand Up @@ -1416,9 +1419,8 @@
</attribute> </attribute>


<attribute name="SSLCipherSuite" required="false"> <attribute name="SSLCipherSuite" required="false">
<p>Ciphers which may be used for communicating with clients. The default <p>This is an alias for the <code>ciphers</code> attribute of the default
is <code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5</code>. See the OpenSSL <a href="#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> element.</p>
documentation for details of the syntax for this attribute.</p>
</attribute> </attribute>


<attribute name="SSLDisableCompression" required="false"> <attribute name="SSLDisableCompression" required="false">
Expand Down

0 comments on commit ef303e3

Please sign in to comment.