【问题标题】:Netty client does not send client certificate during SSL handshake that requires mutual authenticationNetty 客户端在需要相互身份验证的 SSL 握手期间不发送客户端证书
【发布时间】:2016-02-04 08:53:16
【问题描述】:

我是 Netty 的新手,我尝试编写一个使用相互身份验证的回显服务器和客户端。不幸的是,它不起作用,客户端没有发送其客户端证书,并且服务器按预期断开连接。下面是我到目前为止所做的工作和客户端代码的概述 - 这可能包含一些错误或者我错过了一些重要的事情。感谢您经历这一切!

这就是我所拥有的:

  • Netty 版本 4.1.0.CR1
  • 可在服务器上下载的有效密钥库、信任库和 CRL
  • 直接使用 JSSE 的 echo 服务器和客户端的完整实现(按预期工作)
  • 使用 Netty 的 echo 服务器的工作实现(与基于 JSSE 的客户端一起使用时工作正常)
  • 基于 Netty 的客户端,不发送客户端证书

客户端代码:

通道处理程序:

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();
    }
}

通道初始化器:

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());
    }
}

回显客户端:

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();
        }
    }
}

还有一个返回 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;
    }
...

虚拟机参数:

-Djavax.net.debug=all -Djava.security.debug="certpath crl" -Dcom.sun.net.ssl.checkRevocation=true -Dcom.sun.security.enableCRLDP=true

我很确定我的错误一定是在 Netty 客户端代码中,因为仅使用 JSSE 时系统运行良好。非常感谢任何帮助!

干杯, 安迪

【问题讨论】:

    标签: ssl netty


    【解决方案1】:

    好的,我已经开始工作了。实际上是我的客户端代码错误(代码基于 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();
    }   
    

    之前的代码只是过早断开连接,因此握手从未完成。

    【讨论】:

      猜你喜欢
      • 2014-04-14
      • 1970-01-01
      • 2014-11-13
      • 2016-10-11
      • 2011-01-31
      • 1970-01-01
      • 2012-03-07
      • 1970-01-01
      • 2016-06-15
      相关资源
      最近更新 更多