I'm new to Netty and I try to write an echo server and client that uses mutual authentication. Unfortunately, it's not working, the client doesn't send its client certificate and the server disconnects as expected. Below an overview of what I've done so far and the client side code - that probably contains some bug or I missed something important. Thanks for going through all this!
That is what I have:
Client code:
The channel handler:
package info.junius.tutorial.echo.netty.tls;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf>
{
@Override
public void channelRead0(ChannelHandlerContext ctx, ByteBuf in)
{
System.out.println("CLIENT: Received echo from server:\n" + in.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
{
cause.printStackTrace();
ctx.close();
}
}
The channel initialiser:
package info.junius.tutorial.echo.netty.tls;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.ssl.SslContext;
public class ClientChannelInitializer extends ChannelInitializer<Channel>
{
private final SslContext context;
private final String peerHost;
private final int peerPort;
public ClientChannelInitializer(SslContext context, String peerHost, int peerPort)
{
this.context = context;
this.peerHost = peerHost;
this.peerPort = peerPort;
}
@Override
protected void initChannel(Channel channel) throws Exception
{
// Add SSL handler first to encrypt and decrypt everything.
channel.pipeline().addLast(this.context.newHandler(channel.alloc(), this.peerHost, this.peerPort));
// and then business logic.
channel.pipeline().addLast(new EchoClientHandler());
}
}
The echo client:
package info.junius.tutorial.echo.netty.tls;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
public class EchoClient
{
private final String host;
private final int port;
public EchoClient(String host, int port)
{
super();
this.host = host;
this.port = port;
}
public static void main(String[] args) throws Exception
{
if (args.length != 2)
{
System.err.println("Usage: " + EchoClient.class.getSimpleName() + " <host> <port>");
}
else
{
// Security.addProvider(new BouncyCastleProvider());
String host = args[0];
int port = Integer.parseInt(args[1]);
new EchoClient(host, port).start();
}
}
public void start() throws Exception
{
TlsContextUtil tlsContextUtil = new TlsContextUtil();
ChannelInitializer<Channel> channelInitializer = new ClientChannelInitializer(tlsContextUtil.getClientContext(), this.host, this.port);
EventLoopGroup group = new NioEventLoopGroup();
try
{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(channelInitializer);
Channel channel = b.connect(this.host, this.port).sync().channel();
ChannelFuture writeFuture = channel.writeAndFlush("Hello from netty client!\n");
// channel.closeFuture().sync();
writeFuture.sync();
}
finally
{
group.shutdownGracefully().sync();
}
}
}
And a utility class that returns an SslContext:
...
public SslContext getClientContext() throws IOException
{
SslContext sslContext = null;
try
{
// truststore
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", "SunJSSE");
tmf.init(this.getKeystore(TRUSTSTORE));
// keystore holding client certificate
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX", "SunJSSE");
kmf.init(this.getKeystore(CLIENT_KEYSTORE), KEYSTORE_PW);
SslContextBuilder builder = SslContextBuilder.forClient().keyManager(kmf).trustManager(tmf).ciphers(PFS_CIPHERS);
// build context
sslContext = builder.build();
}
catch (NoSuchAlgorithmException
| NoSuchProviderException
| KeyStoreException
| IllegalStateException
| UnrecoverableKeyException e)
{
throw new IOException("Unable to create client TLS context", e);
}
return sslContext;
}
...
VM arguments:
-Djavax.net.debug=all -Djava.security.debug="certpath crl" -Dcom.sun.net.ssl.checkRevocation=true -Dcom.sun.security.enableCRLDP=true
I'm quite confident that my mistake must be in the Netty client code, because the system works fine when using JSSE only. Any help is highly appreciated!
Cheers, Andy
答案 0 :(得分:0)
好的,我已经开始工作了。实际上我的客户端代码是错误的(代码是基于Netty附带的安全聊天示例)。所以我将其更改为echo示例中使用的版本:
EchoClientHandler:
@Override
public void channelActive(ChannelHandlerContext ctx)
{
// When notified that the channel is active send a message.
System.out.println("CLIENT: Sending request to server...");
ctx.writeAndFlush(Unpooled.copiedBuffer("Mein Schnitzel ist kaputt!\n", CharsetUtil.UTF_8));
}
和EchoClient:
try
{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(channelInitializer);
ChannelFuture f = b.connect(this.host, this.port).sync();
f.channel().closeFuture().sync();
}
finally
{
group.shutdownGracefully().sync();
}
之前的代码过早断开,因此握手永远不会完成。