使用 SSL 的 Netty 4 解码器错误
Netty 4 decoder error with SSL
我正在使用 Netty 4.0.30
我已经在模拟器和设备 运行 Android 4.4.4 上测试了我的代码,但我没有看到这个问题。在这些情况下一切正常。
在我的 HTC One 运行 Android 5.02 上,但是每次在我的 SSL 连接期间都会发生这种情况。我对 SSL 完全是菜鸟,我的代码几乎没有根据 netty (https://github.com/netty/netty/tree/4.0/example/src/main/java/io/netty/example/securechat) 提供的 SSL 聊天示例进行修改。
服务器端看到 "SSL channel active" 和 "SSL Operate Complete called" 但不会打印任何进一步的错误或其他信息。客户端看到 "SSL channel active, send is:blah" 然后出错。基本上双方似乎都没有阅读成功。这是来自客户端的堆栈跟踪:
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.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
以下是我的代码片段,尽管实际上它们与示例几乎没有区别,客户端:
// 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");
初始化程序:
@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());
}
处理程序:
@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.
}
服务器:
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();
初始化程序:
@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());
}
处理程序:
@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();
}
感谢任何帮助,我看到了这个并且担心它 related/not 已修复:https://github.com/netty/netty/issues/4116
感谢阅读!
更新
这种行为在模拟器上也是一致的,连接适用于 KitKat 但不适用于 Android > 5.0。我注意到我的(连接工作)4.4.4 设备仅支持 TLSv1 和 SSLv3,而(非工作连接)设备支持 TLSv1.2,我相信正在使用此版本的协议
虽然不理想,但目前的解决方法是使用 Netty 的旧版本 4.0.25,然后填补 SslContextBuilder 的空白,因为它在此版本中不存在。客户端已在 4.0.30 中更改:
final SslContext sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
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;
}
编辑 #2
忽略编辑 #1
我正在使用 Netty 4.0.30 我已经在模拟器和设备 运行 Android 4.4.4 上测试了我的代码,但我没有看到这个问题。在这些情况下一切正常。
在我的 HTC One 运行 Android 5.02 上,但是每次在我的 SSL 连接期间都会发生这种情况。我对 SSL 完全是菜鸟,我的代码几乎没有根据 netty (https://github.com/netty/netty/tree/4.0/example/src/main/java/io/netty/example/securechat) 提供的 SSL 聊天示例进行修改。
服务器端看到 "SSL channel active" 和 "SSL Operate Complete called" 但不会打印任何进一步的错误或其他信息。客户端看到 "SSL channel active, send is:blah" 然后出错。基本上双方似乎都没有阅读成功。这是来自客户端的堆栈跟踪:
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.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
以下是我的代码片段,尽管实际上它们与示例几乎没有区别,客户端:
// 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");
初始化程序:
@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());
}
处理程序:
@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.
}
服务器:
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();
初始化程序:
@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());
}
处理程序:
@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();
}
感谢任何帮助,我看到了这个并且担心它 related/not 已修复:https://github.com/netty/netty/issues/4116 感谢阅读!
更新 这种行为在模拟器上也是一致的,连接适用于 KitKat 但不适用于 Android > 5.0。我注意到我的(连接工作)4.4.4 设备仅支持 TLSv1 和 SSLv3,而(非工作连接)设备支持 TLSv1.2,我相信正在使用此版本的协议
虽然不理想,但目前的解决方法是使用 Netty 的旧版本 4.0.25,然后填补 SslContextBuilder 的空白,因为它在此版本中不存在。客户端已在 4.0.30 中更改:
final SslContext sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
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;
}
编辑 #2 忽略编辑 #1