简体   繁体   English

使用SSL的Netty 4解码器错误

[英]Netty 4 decoder error with SSL

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. 我正在使用Netty 4.0.30,我已经在模拟器和运行Android 4.4.4的设备上测试了我的代码,但没有看到此问题。 Everything works fine in these cases. 在这种情况下,一切正常。

On my HTC One running Android 5.02, however this happens every time during my SSL connection. 在运行Android 5.02的HTC One上,但是每次在SSL连接期间都会发生这种情况。 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 ). 我对SSL完全陌生,从netty提供的SSL聊天示例中几乎未修改我的代码( 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. 服务器端看到“ SSL通道处于活动状态”和“已调用SSL操作完成”,但未显示任何其他错误或其他信息。 The client sees "SSL channel active, send is:blah" and then errors out. 客户端看到“ SSL通道处于活动状态,发送is:blah”,然后出现错误。 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! 任何帮助表示赞赏,我看到了这一点,并担心它是相关的/不固定的: https : //github.com/netty/netty/issues/4116感谢您的阅读!

Update This behavior is consistent on emulators as well, the connection works fine for KitKat but not for Android > 5.0. 更新此行为在仿真器上也是一致的,该连接对于KitKat正常工作,但对于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 我注意到,我的(连接正常)4.4.4设备仅支持TLSv1和SSLv3,而由于(非正常连接)设备支持TLSv1.2,我相信正在使用此版本的协议

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. 尽管不理想,但暂时的解决方法是使用Netty 4.0.25的旧版本,然后填补SslContextBuilder的空白,因为该版本中不存在该版本。 The client has been changed in 4.0.30 from: 客户端已在4.0.30中从以下位置更改:

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

to this in 4.0.25: 在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 编辑#2忽略编辑#1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM