Hi,
I experienced a similar problem when trying to set specific cipher suites in
Restlet 2.1-RC5 using the internal server connector and the Apache client
connector.
Whatever I passed to the server's and client's parameters "enabledCipherSuites"
and "disabledCipherSuites" seemed to be ignored.
I did some debugging, and the results suggest to me that the cause may be a bug
in restlet. As I understand it from the javadoc and the structure of the
method,
DefaultSslContextFactory.init(Series<Parameter> helperParameters)
in the org.restlet.ext.ssl package should extract all relevant parameters into
attributes of the DefaultSslContextFactory. However, this is not the case for
the two parameters "enabledCipherSuites" and "disabledCipherSuites".
I managed to create a local hack fixing this (I haven't touched the restlet
library itself, but created some subclasses overwriting some of the existing
code).
It was not sufficient to just add the missing functionality to the
DefaultSslContext.init method. I had to work in some other places as well in
order to make sure that the two parameters really get passed down to the point
where they matter. In case you are interested, I have attached the code I used
for my local fix. While I have my doubts whether all of this is really suitable
as a general solution yet (in particular, the part where I wrap and override
Java's SSLSocketFactory), I hope that it helps getting closer to the solution
of this issue.
best regards,
Andreas
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=2974368
package prv.asc.web;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import org.restlet.data.Parameter;
import org.restlet.ext.ssl.DefaultSslContextFactory;
import org.restlet.util.Series;
public class DefaultSslContextFactoryWithCipherSelection extends DefaultSslContextFactory {
// essentially copied from superclass; just needed to create my subclass of
// SslContext rather than restlet's DefaultSslContext in the end
@Override
public javax.net.ssl.SSLContext createSslContext() throws Exception {
javax.net.ssl.SSLContext result = null;
javax.net.ssl.KeyManagerFactory kmf = null;
if ((this.getKeyStorePath() != null) || (this.getKeyStoreProvider() != null)
|| (this.getKeyStoreType() != null)) {
// Loads the key store.
KeyStore keyStore = (this.getKeyStoreProvider() != null) ? KeyStore
.getInstance(
(this.getKeyStoreType() != null) ? this.getKeyStoreType()
: KeyStore.getDefaultType(),
this.getKeyStoreProvider())
: KeyStore
.getInstance((this.getKeyStoreType() != null) ? this.getKeyStoreType()
: KeyStore.getDefaultType());
FileInputStream keyStoreInputStream = null;
try {
keyStoreInputStream = ((this.getKeyStorePath() != null) && (!"NONE"
.equals(this.getKeyStorePath()))) ? new FileInputStream(
this.getKeyStorePath()) : null;
keyStore.load(keyStoreInputStream, this.getKeyStorePassword());
} finally {
if (keyStoreInputStream != null) {
keyStoreInputStream.close();
}
}
// Creates the key-manager factory.
kmf = javax.net.ssl.KeyManagerFactory
.getInstance(this.getCertAlgorithm());
kmf.init(keyStore, this.getKeyStoreKeyPassword());
}
javax.net.ssl.TrustManagerFactory tmf = null;
if ((this.getTrustStorePath() != null) || (this.getTrustStoreProvider() != null)
|| (this.getTrustStoreType() != null)) {
// Loads the trust store.
KeyStore trustStore = (this.getTrustStoreProvider() != null) ? KeyStore
.getInstance(
(this.getTrustStoreType() != null) ? this.getTrustStoreType()
: KeyStore.getDefaultType(),
this.getTrustStoreProvider())
: KeyStore
.getInstance((this.getTrustStoreType() != null) ? this.getTrustStoreType()
: KeyStore.getDefaultType());
FileInputStream trustStoreInputStream = null;
try {
trustStoreInputStream = ((this.getTrustStorePath() != null) && (!"NONE"
.equals(this.getTrustStorePath()))) ? new FileInputStream(
this.getTrustStorePath()) : null;
trustStore.load(trustStoreInputStream, this.getTrustStorePassword());
} finally {
if (trustStoreInputStream != null) {
trustStoreInputStream.close();
}
}
// Creates the trust-manager factory.
tmf = javax.net.ssl.TrustManagerFactory
.getInstance(this.getTrustManagerAlgorithm());
tmf.init(trustStore);
}
// Creates the SSL context
javax.net.ssl.SSLContext sslContext = javax.net.ssl.SSLContext
.getInstance(this.getSslProtocol());
SecureRandom sr = null;
if (this.getSecureRandomAlgorithm() != null) {
sr = SecureRandom.getInstance(this.getSecureRandomAlgorithm());
}
sslContext.init(kmf != null ? kmf.getKeyManagers() : null,
tmf != null ? tmf.getTrustManagers() : null, sr);
// Wraps the SSL context to be able to set cipher suites and other
// properties after SSL engine creation for example
result = new DefaultSslContextWithCipherSelection(this, sslContext);
return result;
}
@Override
public void init(Series<Parameter> helperParameters) {
String suites = helperParameters.getFirstValue("enabledCipherSuites", true, null);
setEnabledCipherSuites(suites == null ? null : suites.split("\\s"));
suites = helperParameters.getFirstValue("disabledCipherSuites", true, null);
setDisabledCipherSuites(suites == null ? null : suites.split("\\s"));
super.init(helperParameters);
}
}
package prv.asc.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocketFactory;
import org.restlet.ext.ssl.DefaultSslContextFactory;
import org.restlet.ext.ssl.internal.DefaultSslContextSpi;
public class DefaultSslContextSpiWithCipherSelection extends DefaultSslContextSpi {
public DefaultSslContextSpiWithCipherSelection(DefaultSslContextFactory contextFactory,
SSLContext wrappedContext) {
super(contextFactory, wrappedContext);
}
@Override
protected void initEngine(SSLEngine sslEngine) {
String[] enabledCipherSuites = getContextFactory().getEnabledCipherSuites();
if(enabledCipherSuites == null) {
enabledCipherSuites = getWrappedContext().getDefaultSSLParameters().getCipherSuites();
}
List<String> enabledCipherSuitesList = new ArrayList<String>(Arrays.asList(enabledCipherSuites));
String[] disabledCipherSuites = getContextFactory().getDisabledCipherSuites();
if(disabledCipherSuites != null) {
for(String disabledCipher : disabledCipherSuites) {
enabledCipherSuitesList.remove(disabledCipher);
}
}
String[] actualCipherSuites = new String[enabledCipherSuitesList.size()];
enabledCipherSuitesList.toArray(actualCipherSuites);
sslEngine.setEnabledCipherSuites(actualCipherSuites);
super.initEngine(sslEngine);
}
@Override
protected SSLSocketFactory engineGetSocketFactory() {
SSLSocketFactory innerFactory = getWrappedContext().getSocketFactory();
return new SSLSocketFactoryWithCipherSelection(innerFactory, getContextFactory().getEnabledCipherSuites(), getContextFactory().getDisabledCipherSuites());
}
}
package prv.asc.web;
import javax.net.ssl.SSLContext;
import org.restlet.ext.ssl.DefaultSslContextFactory;
// essentially copied from org.restlet.ext.internal.DefaultSslContext; just needed
// to include my subclass of DefaultSslContextSpi in the data structure
public class DefaultSslContextWithCipherSelection extends SSLContext {
private static DefaultSslContextSpiWithCipherSelection createContextSpi(
DefaultSslContextFactory contextFactory, SSLContext wrappedContext) {
return new DefaultSslContextSpiWithCipherSelection(contextFactory, wrappedContext);
}
public DefaultSslContextWithCipherSelection(DefaultSslContextFactory contextFactory,
SSLContext wrappedContext) {
super(createContextSpi(contextFactory, wrappedContext), wrappedContext
.getProvider(), wrappedContext.getProtocol());
}
}
package prv.asc.web;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class SSLSocketFactoryWithCipherSelection extends SSLSocketFactory {
private SSLSocketFactory innerFactory;
private String[] enabledCipherSuites;
public SSLSocketFactoryWithCipherSelection(SSLSocketFactory innerFactory, String[] enabledCipherSuites, String[] disabledCipherSuites) {
this.innerFactory = innerFactory;
List<String> enabledCipherSuitesList = new ArrayList<String>(Arrays.asList(enabledCipherSuites == null ?
innerFactory.getDefaultCipherSuites() : enabledCipherSuites));
if(disabledCipherSuites != null) {
for(String disabledCipher : disabledCipherSuites) {
enabledCipherSuitesList.remove(disabledCipher);
}
}
this.enabledCipherSuites = new String[enabledCipherSuitesList.size()];
enabledCipherSuitesList.toArray(this.enabledCipherSuites);
}
@Override
public Socket createSocket(Socket s, String host, int port,
boolean autoClose) throws IOException {
SSLSocket socket = (SSLSocket) innerFactory.createSocket(s, host, port, autoClose);
socket.setEnabledCipherSuites(enabledCipherSuites);
return socket;
}
@Override
public String[] getDefaultCipherSuites() {
return innerFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return innerFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(String host, int port) throws IOException,
UnknownHostException {
SSLSocket socket = (SSLSocket) innerFactory.createSocket(host, port);
socket.setEnabledCipherSuites(enabledCipherSuites);
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket socket = (SSLSocket) innerFactory.createSocket(host, port);
socket.setEnabledCipherSuites(enabledCipherSuites);
return socket;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) innerFactory.createSocket(host, port, localHost, localPort);
socket.setEnabledCipherSuites(enabledCipherSuites);
return socket;
}
@Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
SSLSocket socket = (SSLSocket) innerFactory.createSocket(address, port, localAddress, localPort);
socket.setEnabledCipherSuites(enabledCipherSuites);
return socket;
}
@Override
public Socket createSocket() throws IOException {
SSLSocket socket = (SSLSocket) innerFactory.createSocket();
socket.setEnabledCipherSuites(enabledCipherSuites);
return socket;
}
}