Closed GoogleCodeExporter closed 9 years ago
I have added SSL with a couple of changes to the code.
In the NettyAcceptor I have added the SSL Handler as the first of the last
handlers. The context in order to create it comes from a helper class that
loads the certificates and key from the paths specified on mosquette.conf.
It obviously needs some work yet, but with these changes it already works.
NettyAceptor.java
***
public void initialize(IMessaging messaging, final Properties props)
throws IOException {
m_bossGroup = new NioEventLoopGroup();
m_workerGroup = new NioEventLoopGroup();
final NettyMQTTHandler handler = new NettyMQTTHandler();
handler.setMessaging(messaging);
ServerBootstrap b = new ServerBootstrap();
b.group(m_bossGroup, m_workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
SSLContext sslContext = SSLContextFactory
.getSSLContext(props);
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(false);
engine.setEnableSessionCreation(true);
engine.setWantClientAuth(true);
// pipeline.addFirst("metrics", new
// BytesMetricsHandler(m_metricsCollector));
pipeline.addFirst("idleStateHandler",
new IdleStateHandler(0, 0,
Constants.DEFAULT_CONNECT_TIMEOUT));
pipeline.addAfter("idleStateHandler",
"idleEventHandler",
new MoquetteIdleTimoutHandler());
// pipeline.addLast("logger", new
// LoggingHandler("Netty", LogLevel.ERROR));
SslHandler sslHandler = new SslHandler(engine);
pipeline.addLast("ssl", sslHandler);
pipeline.addLast("decoder", new MQTTDecoder());
pipeline.addLast("encoder", new MQTTEncoder());
pipeline.addLast("metrics", new MessageMetricsHandler(
m_metricsCollector));
pipeline.addLast("handler", handler);
}
}).option(ChannelOption.SO_BACKLOG, 128)
.option(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
// Bind and start to accept incoming connections.
// ChannelFuture f = b.bind(Constants.PORT);
ChannelFuture f = b.bind(props.getProperty("host"),
Integer.parseInt(props.getProperty("port")));
LOG.info("Server binded");
f.sync();
} catch (InterruptedException ex) {
LOG.error(null, ex);
}
}
***
SSLContextFactory.java
package org.dna.mqtt.moquette.server.netty;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Properties;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
public class SSLContextFactory {
public static SSLContext getSSLContext(Properties props) throws Exception {
String caCrtFile = props.getProperty("cafile");
String crtFile = props.getProperty("certfile");
String keyFile = props.getProperty("keyfile");
String password = props.getProperty("keypassphrase");
Security.addProvider(new BouncyCastleProvider());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String configPath = System.getProperty("moquette.path", "");
InputStream caCertIS = new FileInputStream(new File(configPath + caCrtFile));
X509Certificate caCert = (X509Certificate) cf
.generateCertificate(caCertIS);
InputStream crtFileIS = new FileInputStream(new File(configPath + crtFile));
X509Certificate cert = (X509Certificate) cf
.generateCertificate(crtFileIS);
File privateKeyFile = new File(configPath + keyFile);
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder()
.build(password.toCharArray());
;
JcaPEMKeyConverter converter = new JcaPEMKeyConverter()
.setProvider("BC");
Object object = pemParser.readObject();
KeyPair kp;
if (object instanceof PEMEncryptedKeyPair) {
kp = converter.getKeyPair(((PEMEncryptedKeyPair) object)
.decryptKeyPair(decProv));
} else {
kp = converter.getKeyPair((PEMKeyPair) object);
}
pemParser.close();
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", kp.getPrivate(), password.toCharArray(),
new java.security.cert.Certificate[] { cert });
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context;
}
}
pom.xml
***
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.48</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.48</version>
</dependency>
***
mosquette.conf
##############################################
# Moquette configuration file.
#
# The synthax is equals to mosquitto.conf
#
##############################################
port 1883
host 0.0.0.0
password_file config/password_file.conf
cafile config/m2mqtt_ca.crt
certfile config/m2mqtt_srv.crt
keyfile config/m2mqtt_srv.key
keypassphrase ******
Original comment by davidmen...@gmail.com
on 24 Apr 2014 at 12:14
Integrated with r=775acff84631ddd74fb26c9709ebbb41e44308ea
Thanks a lot for the suggestion!
Just one question why you preferred BouncyCastle instead of plain JKS?
Andrea
Original comment by selva.an...@gmail.com
on 22 May 2014 at 10:16
Original issue reported on code.google.com by
davidmen...@gmail.com
on 21 Apr 2014 at 12:22