Subash Chaturanga
Subash Chaturanga

Reputation: 834

Netty 4.0.27 HTTP over SSL (as in secure chat sample) fails for multiple clients

I was following the secure chat sample to implement a HTTPS server. For a single client this works fine. With more than one client I am getting netty server side exception [1]. I am using SOAP UI and Jmeter as my two clients. This works fine for either SOAPUI or JMeter.

My server code is as follows.

 SSLEngine engine =   SSLContextFactory.getServerContext().createSSLEngine();
    engine.setUseClientMode(false);

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 200)
                .childOption(ChannelOption.ALLOCATOR,
                        PooledByteBufAllocator.DEFAULT)
                .childHandler(new ServerInitializer(engine));

        ChannelFuture future = bootstrap.bind(8002).sync();
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                System.out.println("#### Bootstrap Bind Future #####");
            }
        });
        ChannelFuture closef = future.channel().closeFuture();
        closef.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                System.out.println("#### Channel Close Future #####");
            }
        });
        closef.sync();

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

        try {
            bossGroup.terminationFuture().sync();
            workerGroup.terminationFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Server Initializer class

public class ServerInitializer extends ChannelInitializer<SocketChannel> {

private SSLEngine sslEngine;

public ServerInitializer(SSLEngine sslEngine) {
    this.sslEngine = sslEngine;
}

@Override
public void initChannel(SocketChannel ch) throws Exception {
  ChannelPipeline pipeline = ch.pipeline();
  pipeline.addLast(new SslHandler(sslEngine));
  pipeline.addLast("decoder", new HttpRequestDecoder());
    pipeline.addLast("aggregator", new                    HttpObjectAggregator(Integer.MAX_VALUE));
  pipeline.addLast("encoder", new HttpResponseEncoder());
  pipeline.addLast(new StreamHttpServerHandler());

 }
 }

And my SSLCOntext class

public class SSLContextFactory {

private static final String PROTOCOL = "TLS";
private static final String KEY_TYPE = "JKS";

private static final SSLContext SERVER_CONTEXT;
private static final SSLContext CLIENT_CONTEXT;
private static final TrustManagerFactory trustManagerFactory;

static {

    SSLContext serverContext = null;
    SSLContext clientContext = null;

    try {

        KeyStore ks = KeyStore.getInstance(KEY_TYPE);
           ks.load(new    ByteArrayInputStream(Base64.decodeBase64(SSLKey.getKey())), new    String(Base64.decodeBase64(SSLKey.getKeyPass())).toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, new   String(Base64.decodeBase64(SSLKey.getKeyPass())).toCharArray());

        KeyStore ts = KeyStore.getInstance(KEY_TYPE);
        ts.load(new ByteArrayInputStream(Base64.decodeBase64(SSLKey.getKey())), new String(Base64.decodeBase64(SSLKey.getKeyPass())).toCharArray());

        trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(ts);

        serverContext = SSLContext.getInstance(PROTOCOL);    serverContext.init(kmf.getKeyManagers(),trustManagerFactory.getTrustManagers(), null);
        clientContext = SSLContext.getInstance(PROTOCOL);
        clientContext.init(null, trustManagerFactory.getTrustManagers(), null);
    } catch (Exception e) {
        throw new Error("Failed to initialize the Netty SSLContext", e);
    }

    SERVER_CONTEXT = serverContext;
    CLIENT_CONTEXT = clientContext;
}


public static SSLContext getServerContext() {
    return SERVER_CONTEXT;
}

public static SSLContext getClientContext() {
    return CLIENT_CONTEXT;
}

public static TrustManagerFactory getTrustManagerFactory() {
    return trustManagerFactory;
}

private SSLContextFactory() {
}

[1]

    io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: ciphertext sanity check failed
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:346)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:229)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:339)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:324)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:847)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
  Caused by: javax.net.ssl.SSLHandshakeException: ciphertext sanity check failed
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1683)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:959)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:884)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1114)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:981)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:934)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:315)
... 12 more
     Caused by: javax.crypto.BadPaddingException: ciphertext sanity check failed
at sun.security.ssl.InputRecord.decrypt(InputRecord.java:147)
at sun.security.ssl.EngineInputRecord.decrypt(EngineInputRecord.java:192)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:953)
... 19 more

Upvotes: 0

Views: 1641

Answers (1)

trustin
trustin

Reputation: 12351

You must create a new SSLEngine for each SslHandler. Instead of passing an SSLEngine to the ServerInitializer, pass SslContext and create a new SslHandler via SslContext.newHandler() in ServerInitializer.initChannel(). For more information, please take a look at the SecureChat example:

https://github.com/netty/netty/blob/4.0/example/src/main/java/io/netty/example/securechat/SecureChatServerInitializer.java

Upvotes: 3

Related Questions