3
votes

I am using Netty 4.0.30 I have tested my code on an emulator and a device running Android 4.4.4 and I do not see this issue. Everything works fine in these cases.

On my HTC One running Android 5.02, however this happens every time during my SSL connection. I'm a total noob to SSL, my code is barely modified from the SSL chat example provided by netty (https://github.com/netty/netty/tree/4.0/example/src/main/java/io/netty/example/securechat).

The server side sees "SSL channel active" and "SSL Operate Complete called" but does not print anything further errors or otherwise. The client sees "SSL channel active, send is:blah" and then errors out. Basically neither side seems to read successfully. Here is the stack trace, from the client side:

W/System.err: io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: javax.net.ssl.SSLProtocolException: Read error: ssl=0xb8c20ae0: Failure in SSL library, usually a protocol error
W/System.err: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac (external/openssl/ssl/s3_pkt.c:485 0xb150b0dd:0x00000000)
W/System.err:     at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:358)
W/System.err:     at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:230)
W/System.err:     at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
W/System.err:     at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
W/System.err:     at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)
W/System.err:     at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
W/System.err:     at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
W/System.err:     at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:430)
W/System.err:     at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:384)
W/System.err:     at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
W/System.err:     at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:110)
W/System.err:     at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
W/System.err:     at java.lang.Thread.run(Thread.java:818)
W/System.err: Caused by: javax.net.ssl.SSLException: javax.net.ssl.SSLProtocolException: Read error: ssl=0xb8c20ae0: Failure in SSL library, usually a protocol error
W/System.err: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac (external/openssl/ssl/s3_pkt.c:485 0xb150b0dd:0x00000000)
W/System.err:     at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:491)
W/System.err:     at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:1006)
W/System.err:     at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1129)
W/System.err:     at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1019)
W/System.err:     at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:959)
W/System.err:     at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:327)
W/System.err:   ... 12 more
W/System.err: Caused by: javax.net.ssl.SSLProtocolException: Read error: ssl=0xb8c20ae0: Failure in SSL library, usually a protocol error
W/System.err: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac (external/openssl/ssl/s3_pkt.c:485 0xb150b0dd:0x00000000)
W/System.err:     at com.android.org.conscrypt.NativeCrypto.SSL_read_BIO(Native Method)
W/System.err:     at com.android.org.conscrypt.OpenSSLEngineImpl.unwrap(OpenSSLEngineImpl.java:472)
W/System.err:   ... 17 more

Here are snippets from my code, although really they are hardly different from the example, Client side:

        // Configure SSL.
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        final SslContext sslCtx = SslContextBuilder.forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE).build();

        Bootstrap b = new Bootstrap();
        b.group(group)
                .channel(NioSocketChannel.class)
                .handler(new SSLClientInitializer(sslCtx,server,port));

        // Start the connection attempt.
        Channel ch = b.connect(server, port).sync().channel();

        // Wait until the connection is closed.
        ch.closeFuture().sync();
        System.out.println("SSL closed");

Initializer:

   @Override
public void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();

    // Add SSL handler first to encrypt and decrypt everything.
    // In this example, we use a bogus certificate in the server side
    // and accept any invalid certificates in the client side.
    // You will need something more complicated to identify both
    // and server in the real world.
    pipeline.addLast(sslCtx.newHandler(ch.alloc(), server, port));

    // On top of the SSL handler, add the text line codec.
    pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    pipeline.addLast(new StringDecoder());
    pipeline.addLast(new StringEncoder());

    // and then business logic.
    pipeline.addLast(new SSLClientHandler());
}

Handler:

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    super.channelActive(ctx);
    String send = "blah"
    System.out.println("SSL channel active, send is:"+send);
    ctx.channel().writeAndFlush(send);
}

@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    System.out.println("SSL read0:" + msg);
    ctx.close();//close this channel.

}

Server:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        SelfSignedCertificate ssc = new SelfSignedCertificate();//@todo need a real cert
        SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
                .build();

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                //.handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new SSLServerInitializer(sslCtx));

        b.bind(port).sync().channel().closeFuture().sync();

Initializer:

@Override
public void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();

    // Add SSL handler first to encrypt and decrypt everything.
    // In this example, we use a bogus certificate in the server side
    // and accept any invalid certificates in the client side.
    // You will need something more complicated to identify both
    // and server in the real world.
    pipeline.addLast(sslCtx.newHandler(ch.alloc()));

    // On top of the SSL handler, add the text line codec.
    pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    pipeline.addLast(new StringDecoder());
    pipeline.addLast(new StringEncoder());

    // and then business logic.
    pipeline.addLast(new SSLServerHandler());
}

Handler:

@Override
public void channelActive(final ChannelHandlerContext ctx) {
    // Once session is secured, send a greeting and register the channel to the global channel
    // list so the channel received the messages from others.
    System.out.println("SSL channel active");
    ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
            new GenericFutureListener<Future<Channel>>() {
                @Override
                public void operationComplete(Future<Channel> future) throws Exception {
                    System.out.println("SSL Operate Complete called");
                    ctx.writeAndFlush("Your session is protected by " +
                                    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 {
    System.out.println("SSL read0:" + msg);
    ctx.writeAndFlush("granted\n");

    ctx.close();

}

Any help is appreciated, I saw this and am worried it is related/not fixed: https://github.com/netty/netty/issues/4116 Thanks for reading!

Update This behavior is consistent on emulators as well, the connection works fine for KitKat but not for Android > 5.0. I have noticed that my (connection working) 4.4.4 device only supports TLSv1 and SSLv3, where as the (non working connection) devices support TLSv1.2 and I believe are using this version of the protocol

1
Was able to test this on another phone, LG G2 running Android 5.02, the same as my HTC one. This phone also exhibited the same error. Looking for a work around. - M1LKYW4Y

1 Answers

0
votes

Although not ideal, a work around for now is to use the older version of Netty, 4.0.25, and then fill in the gaps for SslContextBuilder since it does not exist in this version. The client has been changed in 4.0.30 from:

final SslContext sslCtx = SslContextBuilder.forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE).build();

to this in 4.0.25:

final SslContext sslCtx;
        TrustManagerFactory trustManagerFactory = InsecureTrustManagerFactory.INSTANCE;
        SslProvider provider = SslContext.defaultClientProvider();
        File trustCertChainFile = null;
        File keyCertChainFile = null;
        File keyFile = null;
        String keyPassword = null;
        KeyManagerFactory keyManagerFactory = null;
        Iterable<String> ciphers = null;
        ApplicationProtocolConfig apn = null;
        switch (provider) {
            case JDK:
                sslCtx = new JdkSslClientContext(
                        trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
                        keyManagerFactory, ciphers, IdentityCipherSuiteFilter.INSTANCE, apn, 0, 0);
                break;
            case OPENSSL:
            default:
                sslCtx = new OpenSslClientContext(
                        trustCertChainFile, trustManagerFactory,
                        ciphers, apn, 0, 0);
                break;
        }

Edit #2 Disregard Edit #1