簡體   English   中英

Netty客戶端在需要相互認證的SSL握手期間不發送客戶端證書

[英]Netty client does not send client certificate during SSL handshake that requires mutual authentication

我是Netty的新手,我嘗試編寫使用雙向身份驗證的echo服務器和客戶端。 不幸的是,它無法正常工作,客戶端沒有發送其客戶端證書,並且服務器按預期斷開了連接。 下面是到目前為止我所做的工作和客戶端代碼的概述-可能包含一些錯誤,或者我錯過了一些重要的事情。 感謝您完成所有這一切!

那就是我所擁有的:

  • 網絡版本4.1.0.CR1
  • 有效的密鑰庫,信任庫和CRL可在服務器上下載
  • 直接使用JSSE的回顯服務器和客戶端的完整實現(按預期工作)
  • 使用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;
    }
...

VM參數:

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

我非常有信心,我的錯誤一定是在Netty客戶端代碼中,因為僅使用JSSE時系統運行良好。 任何幫助深表感謝!

干杯,安迪

好,我已經開始工作了。 實際上是我的客戶代碼錯了(該代碼基於Netty附帶的安全聊天示例)。 因此,我將其更改為回顯示例中使用的版本:

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

先前的代碼斷開時間太早,因此握手從未完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM