java - Netty SSL: Chat Client example with custom keystore is unable to accept multiple connections -
java - Netty SSL: Chat Client example with custom keystore is unable to accept multiple connections -
i've been playing netty's (version 4.0.24) securechatserver examples , i've plugged in own keystore (based on answers found on next 2 posts post 1 , post2. see code snippets below.
the issue i'm seeing works expected when run 1 instance of client, when seek launch more client instances exceptions on both server , client (see below exception text) , @ point server stops responding.
i'm hoping here shed lite i'm doing wrong.
this class handles loading keystore , creating sslcontext instances used both client/server (for obvious reasons i've omitted keystore values):
package com.test.securechat; import java.io.bytearrayinputstream; import java.security.keystore; import javax.net.ssl.keymanagerfactory; import javax.net.ssl.sslcontext; import javax.net.ssl.trustmanagerfactory; import com.test.util.key; import com.pragmafs.util.encryption.base64coder; /** * creates sslcontext instances static (string representation) keystore * see http://maxrohde.com/2013/09/07/setting-up-ssl-with-netty/ */ public class sslcontextfactory { private static final string protocol = "tls"; private static final sslcontext server_context; private static final sslcontext client_context; private static final trustmanagerfactory trustmanagerfactory; static { sslcontext servercontext = null; sslcontext clientcontext = null; seek { keystore ks = keystore.getinstance("jks"); ks.load(new bytearrayinputstream(base64coder.decode(key.ssl.getkey())), base64coder.decodestring(key.ssl.getpwd()).tochararray()); // set key manager mill utilize our key store keymanagerfactory kmf = keymanagerfactory.getinstance(keymanagerfactory.getdefaultalgorithm()); kmf.init(ks, base64coder.decodestring(key.ssl.getpwd()).tochararray()); // truststore keystore ts = keystore.getinstance("jks"); ts.load(new bytearrayinputstream(base64coder.decode(key.ssl.getkey())), base64coder.decodestring(key.ssl.getpwd()).tochararray()); // set trust manager mill utilize our trust store trustmanagerfactory = trustmanagerfactory.getinstance(trustmanagerfactory.getdefaultalgorithm()); trustmanagerfactory.init(ts); // initialize sslcontext work our key managers. servercontext = sslcontext.getinstance(protocol); servercontext.init(kmf.getkeymanagers(), trustmanagerfactory.gettrustmanagers(), null); clientcontext = sslcontext.getinstance(protocol); clientcontext.init(null, trustmanagerfactory.gettrustmanagers(), null); } grab (exception e) { throw new error("failed initialize sslcontext", e); } server_context = servercontext; client_context = clientcontext; } public static sslcontext getservercontext() { homecoming server_context; } public static sslcontext getclientcontext() { homecoming client_context; } public static trustmanagerfactory gettrustmanagerfactory() { homecoming trustmanagerfactory; } private sslcontextfactory() { // unused } }
this server code:
package com.test.securechat; import java.net.inetaddress; import javax.net.ssl.sslengine; import io.netty.bootstrap.serverbootstrap; import io.netty.channel.channel; import io.netty.channel.channelhandlercontext; import io.netty.channel.channelinitializer; import io.netty.channel.channelpipeline; import io.netty.channel.eventloopgroup; import io.netty.channel.simplechannelinboundhandler; import io.netty.channel.group.channelgroup; import io.netty.channel.group.defaultchannelgroup; import io.netty.channel.nio.nioeventloopgroup; import io.netty.channel.socket.socketchannel; import io.netty.channel.socket.nio.nioserversocketchannel; import io.netty.handler.codec.delimiterbasedframedecoder; import io.netty.handler.codec.delimiters; import io.netty.handler.codec.string.stringdecoder; import io.netty.handler.codec.string.stringencoder; import io.netty.handler.logging.loglevel; import io.netty.handler.logging.logginghandler; import io.netty.handler.ssl.sslhandler; import io.netty.util.concurrent.future; import io.netty.util.concurrent.genericfuturelistener; import io.netty.util.concurrent.globaleventexecutor; /** * simple ssl chat server */ public final class securechatserver { static final int port = integer.parseint(system.getproperty("port", "8992")); public static void main(string[] args) throws exception { sslengine sslengine = sslcontextfactory.getservercontext().createsslengine(); sslengine.setuseclientmode(false); eventloopgroup bossgroup = new nioeventloopgroup(1); eventloopgroup workergroup = new nioeventloopgroup(); seek { serverbootstrap b = new serverbootstrap(); b.group(bossgroup, workergroup) .channel(nioserversocketchannel.class) .handler(new logginghandler(loglevel.info)) .childhandler(new securechatserverinitializer(sslengine)); b.bind(port).sync().channel().closefuture().sync(); } { bossgroup.shutdowngracefully(); workergroup.shutdowngracefully(); } } private static class securechatserverinitializer extends channelinitializer<socketchannel> { private final sslengine sslctx; public securechatserverinitializer(sslengine sslctx) { this.sslctx = sslctx; } @override public void initchannel(socketchannel ch) throws exception { channelpipeline pipeline = ch.pipeline(); pipeline.addlast(new sslhandler(sslctx)); pipeline.addlast(new delimiterbasedframedecoder(8192, delimiters.linedelimiter())); pipeline.addlast(new stringdecoder()); pipeline.addlast(new stringencoder()); // , business logic. pipeline.addlast(new securechatserverhandler()); } } private static class securechatserverhandler extends simplechannelinboundhandler<string> { static final channelgroup channels = new defaultchannelgroup(globaleventexecutor.instance); @override public void channelactive(final channelhandlercontext ctx) { // 1 time session secured, send greeting , register channel global channel // list channel received messages others. ctx.pipeline().get(sslhandler.class).handshakefuture().addlistener( new genericfuturelistener<future<channel>>() { @override public void operationcomplete(future<channel> future) throws exception { ctx.writeandflush( "welcome " + inetaddress.getlocalhost().gethostname() + " secure chat service!\n"); ctx.writeandflush( "your session protected " + ctx.pipeline().get(sslhandler.class).engine().getsession().getciphersuite() + " cipher suite.\n"); channels.add(ctx.channel()); } }); } @override public void channelread0(channelhandlercontext ctx, string msg) throws exception { // send received message channels current one. (channel c : channels) { if (c != ctx.channel()) { c.writeandflush("[" + ctx.channel().remoteaddress() + "] " + msg + '\n'); } else { c.writeandflush("[you] " + msg + '\n'); } } // close connection if client has sent 'bye'. if ("bye".equals(msg.tolowercase())) { ctx.close(); } } @override public void exceptioncaught(channelhandlercontext ctx, throwable cause) { cause.printstacktrace(); ctx.close(); } } }
client code:
package com.test.securechat; import java.io.bufferedreader; import java.io.inputstreamreader; import javax.net.ssl.sslengine; import io.netty.bootstrap.bootstrap; import io.netty.channel.channel; import io.netty.channel.channelfuture; import io.netty.channel.channelhandlercontext; import io.netty.channel.channelinitializer; import io.netty.channel.channelpipeline; import io.netty.channel.eventloopgroup; import io.netty.channel.simplechannelinboundhandler; import io.netty.channel.nio.nioeventloopgroup; import io.netty.channel.socket.socketchannel; import io.netty.channel.socket.nio.niosocketchannel; import io.netty.handler.codec.delimiterbasedframedecoder; import io.netty.handler.codec.delimiters; import io.netty.handler.codec.string.stringdecoder; import io.netty.handler.codec.string.stringencoder; import io.netty.handler.ssl.sslhandler; /** * simple ssl chat client */ public final class securechatclient { static final string host = system.getproperty("host", "127.0.0.1"); static final int port = integer.parseint(system.getproperty("port", "8992")); public static void main(string[] args) throws exception { sslengine engine = sslcontextfactory.getclientcontext().createsslengine(); engine.setuseclientmode(true); eventloopgroup grouping = new nioeventloopgroup(); seek { bootstrap b = new bootstrap(); b.group(group) .channel(niosocketchannel.class) .handler(new securechatclientinitializer(engine)); // start connection attempt. channel ch = b.connect(host, port).sync().channel(); // read commands stdin. channelfuture lastwritefuture = null; bufferedreader in = new bufferedreader(new inputstreamreader(system.in)); (; ; ) { string line = in.readline(); if (line == null) { break; } // sends received line server. lastwritefuture = ch.writeandflush(line + "\r\n"); // if user typed 'bye' command, wait until server closes // connection. if ("bye".equals(line.tolowercase())) { ch.closefuture().sync(); break; } } // wait until messages flushed before closing channel. if (lastwritefuture != null) { lastwritefuture.sync(); } } { // connection closed automatically on shutdown. group.shutdowngracefully(); } } private static class securechatclientinitializer extends channelinitializer<socketchannel> { private final sslengine sslctx; public securechatclientinitializer(sslengine sslctx) { this.sslctx = sslctx; } @override public void initchannel(socketchannel ch) throws exception { channelpipeline pipeline = ch.pipeline(); // add together ssl handler first encrypt , decrypt everything. // in example, utilize bogus certificate in server side // , take invalid certificates in client side. // need more complicated identify both // , server in real world. //pipeline.addlast(sslctx.newhandler(ch.alloc(), securechatclient.host, securechatclient.port)); pipeline.addlast(new sslhandler(sslctx)); // on top of ssl handler, add together text line codec. pipeline.addlast(new delimiterbasedframedecoder(8192, delimiters.linedelimiter())); pipeline.addlast(new stringdecoder()); pipeline.addlast(new stringencoder()); //pipeline.addlast(new logginghandler(loglevel.info)); // , business logic. pipeline.addlast(new securechatclienthandler()); } } private static class securechatclienthandler extends simplechannelinboundhandler<string> { @override public void channelread0(channelhandlercontext ctx, string msg) throws exception { system.err.println(msg); } @override public void exceptioncaught(channelhandlercontext ctx, throwable cause) { cause.printstacktrace(); ctx.close(); } } }
server exception:
info: [id: 0x5eb2ac7a, /0:0:0:0:0:0:0:0:8992] received: [id: 0x7d7a7dca, /127.0.0.1:55932 => /127.0.0.1:8992] io.netty.handler.codec.decoderexception: javax.net.ssl.sslhandshakeexception: ciphertext sanity check failed @ io.netty.handler.codec.bytetomessagedecoder.calldecode(bytetomessagedecoder.java:278) @ io.netty.handler.codec.bytetomessagedecoder.channelread(bytetomessagedecoder.java:147) @ io.netty.channel.abstractchannelhandlercontext.invokechannelread(abstractchannelhandlercontext.java:333) @ io.netty.channel.abstractchannelhandlercontext.firechannelread(abstractchannelhandlercontext.java:319) @ io.netty.channel.defaultchannelpipeline.firechannelread(defaultchannelpipeline.java:787) @ io.netty.channel.nio.abstractniobytechannel$niobyteunsafe.read(abstractniobytechannel.java:130) @ io.netty.channel.nio.nioeventloop.processselectedkey(nioeventloop.java:511) @ io.netty.channel.nio.nioeventloop.processselectedkeysoptimized(nioeventloop.java:468) @ io.netty.channel.nio.nioeventloop.processselectedkeys(nioeventloop.java:382) @ io.netty.channel.nio.nioeventloop.run(nioeventloop.java:354) @ io.netty.util.concurrent.singlethreadeventexecutor$2.run(singlethreadeventexecutor.java:116) @ io.netty.util.concurrent.defaultthreadfactory$defaultrunnabledecorator.run(defaultthreadfactory.java:137) @ java.lang.thread.run(thread.java:744) caused by: javax.net.ssl.sslhandshakeexception: ciphertext sanity check failed @ sun.security.ssl.alerts.getsslexception(alerts.java:192) @ sun.security.ssl.sslengineimpl.fatal(sslengineimpl.java:1683) @ sun.security.ssl.sslengineimpl.readrecord(sslengineimpl.java:959) @ sun.security.ssl.sslengineimpl.readnetrecord(sslengineimpl.java:884) @ sun.security.ssl.sslengineimpl.unwrap(sslengineimpl.java:758) @ javax.net.ssl.sslengine.unwrap(sslengine.java:624) @ io.netty.handler.ssl.sslhandler.unwrap(sslhandler.java:995) @ io.netty.handler.ssl.sslhandler.unwrap(sslhandler.java:921) @ io.netty.handler.ssl.sslhandler.decode(sslhandler.java:867) @ io.netty.handler.codec.bytetomessagedecoder.calldecode(bytetomessagedecoder.java:247) ... 12 more caused by: javax.crypto.badpaddingexception: ciphertext sanity check failed @ sun.security.ssl.inputrecord.decrypt(inputrecord.java:147) @ sun.security.ssl.engineinputrecord.decrypt(engineinputrecord.java:192) @ sun.security.ssl.sslengineimpl.readrecord(sslengineimpl.java:953) ... 19 more
client exception:
io.netty.handler.codec.decoderexception: javax.net.ssl.sslexception: received fatal alert: <unknown alert: 170> @ io.netty.handler.codec.bytetomessagedecoder.calldecode(bytetomessagedecoder.java:278) @ io.netty.handler.codec.bytetomessagedecoder.channelread(bytetomessagedecoder.java:147) @ io.netty.channel.abstractchannelhandlercontext.invokechannelread(abstractchannelhandlercontext.java:333) @ io.netty.channel.abstractchannelhandlercontext.firechannelread(abstractchannelhandlercontext.java:319) @ io.netty.channel.defaultchannelpipeline.firechannelread(defaultchannelpipeline.java:787) @ io.netty.channel.nio.abstractniobytechannel$niobyteunsafe.read(abstractniobytechannel.java:130) @ io.netty.channel.nio.nioeventloop.processselectedkey(nioeventloop.java:511) @ io.netty.channel.nio.nioeventloop.processselectedkeysoptimized(nioeventloop.java:468) @ io.netty.channel.nio.nioeventloop.processselectedkeys(nioeventloop.java:382) @ io.netty.channel.nio.nioeventloop.run(nioeventloop.java:354) @ io.netty.util.concurrent.singlethreadeventexecutor$2.run(singlethreadeventexecutor.java:116) @ io.netty.util.concurrent.defaultthreadfactory$defaultrunnabledecorator.run(defaultthreadfactory.java:137) @ java.lang.thread.run(thread.java:744) caused by: javax.net.ssl.sslexception: received fatal alert: <unknown alert: 170> @ sun.security.ssl.alerts.getsslexception(alerts.java:208) @ sun.security.ssl.sslengineimpl.fatal(sslengineimpl.java:1619) @ sun.security.ssl.sslengineimpl.fatal(sslengineimpl.java:1587) @ sun.security.ssl.sslengineimpl.recvalert(sslengineimpl.java:1756) @ sun.security.ssl.sslengineimpl.readrecord(sslengineimpl.java:1060) @ sun.security.ssl.sslengineimpl.readnetrecord(sslengineimpl.java:884) @ sun.security.ssl.sslengineimpl.unwrap(sslengineimpl.java:758) @ javax.net.ssl.sslengine.unwrap(sslengine.java:624) @ io.netty.handler.ssl.sslhandler.unwrap(sslhandler.java:995) @ io.netty.handler.ssl.sslhandler.unwrap(sslhandler.java:921) @ io.netty.handler.ssl.sslhandler.decode(sslhandler.java:867) @ io.netty.handler.codec.bytetomessagedecoder.calldecode(bytetomessagedecoder.java:247) ... 12 more
can explain why initializing server sslcontext
trust mill not supplying key mill client sslcontext
initialization method?
are attempting mutual authentication? if need phone call sslengine.setneedclientauth(true)
.
have seen sslcontext.java? may able simplify initialization process , alleviate need build own sslcontext
objects. if certificates x509 , keys in pkcs#8 format don't have worry trust manager or key managers because sslcontext can read these , initialize (via newclientcontext
, newservercontext
). these interfaces back upwards mutual authentication if needed.
java ssl client netty server
Comments
Post a Comment