Enables client authentification for SSL

Client authentification can be set as with the SSL Properties to
require the client certificate to be signed by the server.
Added jUnit test and test certificates
This commit is contained in:
Michael Konrad
2016-02-17 12:02:52 +01:00
parent 368100f77c
commit f5a3d57afd
7 changed files with 230 additions and 4 deletions
@@ -41,4 +41,5 @@ public class BrokerConstants {
public static final int PORT = 1883;
public static final int WEBSOCKET_PORT = 8080;
public static final String HOST = "0.0.0.0";
public static final String NEED_CLIENT_AUTH = "need_client_auth";
}
@@ -23,6 +23,9 @@ import org.slf4j.LoggerFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.net.URL;
import java.security.*;
@@ -69,6 +72,11 @@ class DefaultMoquetteSslContextCreator implements ISslContextCreator {
return null;
}
// if client authentification is enabled a trustmanager needs to be
// added to the ServerContext
String sNeedsClientAuth = props.getProperty(BrokerConstants.NEED_CLIENT_AUTH, "false");
boolean needsClientAuth = Boolean.valueOf(sNeedsClientAuth);
try {
InputStream jksInputStream = jksDatastore(jksPath);
SSLContext serverContext = SSLContext.getInstance("TLS");
@@ -76,7 +84,16 @@ class DefaultMoquetteSslContextCreator implements ISslContextCreator {
ks.load(jksInputStream, keyStorePassword.toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyManagerPassword.toCharArray());
serverContext.init(kmf.getKeyManagers(), null, null);
TrustManager[] trustManagers = null;
if (needsClientAuth) {
// use keystore as truststore, as server needs to trust certificates signed by the server certificates
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
trustManagers = tmf.getTrustManagers();
}
// init sslContext
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
return serverContext;
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | KeyStoreException
| KeyManagementException | IOException ex) {
@@ -205,10 +205,12 @@ public class NettyAcceptor implements ServerAcceptor {
final MoquetteIdleTimeoutHandler timeoutHandler = new MoquetteIdleTimeoutHandler();
String host = props.getProperty(BrokerConstants.HOST_PROPERTY_NAME);
String sNeedsClientAuth = props.getProperty(BrokerConstants.NEED_CLIENT_AUTH, "false");
final boolean needsClientAuth = Boolean.valueOf(sNeedsClientAuth);
initFactory(host, sslPort, new PipelineInitializer() {
@Override
void init(ChannelPipeline pipeline) throws Exception {
pipeline.addLast("ssl", createSslHandler(sslContext));
pipeline.addLast("ssl", createSslHandler(sslContext, needsClientAuth));
pipeline.addFirst("idleStateHandler", new IdleStateHandler(0, 0, Constants.DEFAULT_CONNECT_TIMEOUT));
pipeline.addAfter("idleStateHandler", "idleEventHandler", timeoutHandler);
//pipeline.addLast("logger", new LoggingHandler("Netty", LogLevel.ERROR));
@@ -231,10 +233,12 @@ public class NettyAcceptor implements ServerAcceptor {
int sslPort = Integer.parseInt(sslPortProp);
final MoquetteIdleTimeoutHandler timeoutHandler = new MoquetteIdleTimeoutHandler();
String host = props.getProperty(BrokerConstants.HOST_PROPERTY_NAME);
String sNeedsClientAuth = props.getProperty(BrokerConstants.NEED_CLIENT_AUTH, "false");
final boolean needsClientAuth = Boolean.valueOf(sNeedsClientAuth);
initFactory(host, sslPort, new PipelineInitializer() {
@Override
void init(ChannelPipeline pipeline) throws Exception {
pipeline.addLast("ssl", createSslHandler(sslContext));
pipeline.addLast("ssl", createSslHandler(sslContext, needsClientAuth));
pipeline.addLast("httpEncoder", new HttpResponseEncoder());
pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
@@ -281,9 +285,12 @@ public class NettyAcceptor implements ServerAcceptor {
LOG.info(String.format("Bytes read: %d, bytes wrote: %d", bytesMetrics.readBytes(), bytesMetrics.wroteBytes()));
}
private ChannelHandler createSslHandler(SSLContext sslContext) {
private ChannelHandler createSslHandler(SSLContext sslContext, boolean needsClientAuth) {
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
if(needsClientAuth) {
sslEngine.setNeedClientAuth(true);
}
return new SslHandler(sslEngine);
}
}