netty实现TLS/SSL双向加密认证

示例工程及测试用的证书文件:https://download.csdn.net/download/zhangfls/12875187

openssl在window本地命令行生成证书的方式:https://blog.csdn.net/zhangfls/article/details/108733072

1、双向加密认证首先要获取到证书,可以先自己生成证书用于测试(实际获取到的公网证书使用方式其实差不多

(1)可以通过openssl生成证书

(2)首先要生成一份CA根证书,再由该证书生成服务器和客户端的证书

(3)完成基本的SSL/TLS服务器和客户端的双向加密通讯,一共需要生成5份证书

    ①CA证书

    ②服务器证书

    ③服务器密钥

    ④客户端证书

    ⑤客户端密钥

(4)、一般证书有多种格式,这里我们用pem格式做示例(linux系统常用)

 

2、假设我们已经配置了证书,同时配置好了一个简单的nettyTCP连接的客户端和服务器,下面则需要重新配置netty服务器与客户端的SSL管道
(1)、配置netty的SSL可以通过添加pipeline的方式完成,将管道配置在ChannelInitializer实现类的最前面即可

public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {

    private SslContext sslContext;

    public MyChannelInitializer(SslContext sslContext) {
        this.sslContext = sslContext;
    }

    @Override
    protected void initChannel(SocketChannel channel) {

        // 添加SSL安装验证
        channel.pipeline().addFirst(sslContext.newHandler(channel.alloc()));
        //直接解析原始HEX字节
        channel.pipeline().addLast(new StringEncoder(StandardCharsets.ISO_8859_1));
        //自定义解析数据handler
        channel.pipeline().addLast(new MyServerHandler());
        channel.pipeline().addLast(new ByteArrayEncoder());
    }
}

 

(2)包含证书信息的SslContext可以由SslContextBuilder来生成,可以分别生成客户端及服务器的SSL验证protocol。例如下面的certChainFile 为客户端证书,keyFile为客户端密钥,rootFile为CA根证书,服务器的证书配置同理

        //引入SSL安全验证
        File certChainFile = new File("D:\\OpenSSL-Win64\\bin\\client\\client-cert.pem");
        File keyFile = new File("D:\\OpenSSL-Win64\\bin\\client\\client-key.pem");
        File rootFile = new File("D:\\OpenSSL-Win64\\bin\\ca\\ca-cert.pem");
        
        SslContext sslCtx = SslContextBuilder.forClient().keyManager(certChainFile, keyFile).trustManager(rootFile).build();

 

3、一般做双向验证,服务器端可能还会需要客户端证书的详细信息,来确认客户端的身份。可以通过在连接握手完成时,读取SSLSession的信息,来获取到客户端证书的详细信息,如下:

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
                new GenericFutureListener<Future<Channel>>() {
                    @Override
                    public void operationComplete(Future<Channel> future) throws Exception {
                        if(future.isSuccess()){
                            System.out.println("握手成功");

                            SSLSession ss =  ctx.pipeline().get(SslHandler.class).engine().getSession();
                            System.out.println("cipherSuite:"+ss.getCipherSuite());
                            X509Certificate cert = ss.getPeerCertificateChain()[0];
                            String info = null;
                            // 获得证书版本
                            info = String.valueOf(cert.getVersion());
                            System.out.println("证书版本:" + info);
                            // 获得证书序列号
                            info = cert.getSerialNumber().toString(16);
                            System.out.println("证书序列号:" + info);
                            // 获得证书有效期
                            Date beforedate = cert.getNotBefore();
                            info = new SimpleDateFormat("yyyy/MM/dd").format(beforedate);
                            System.out.println("证书生效日期:" + info);
                            Date afterdate = (Date) cert.getNotAfter();
                            info = new SimpleDateFormat("yyyy/MM/dd").format(afterdate);
                            System.out.println("证书失效日期:" + info);
                            // 获得证书主体信息
                            info = cert.getSubjectDN().getName();
                            System.out.println("证书拥有者:" + info);
                            // 获得证书颁发者信息
                            info = cert.getIssuerDN().getName();
                            System.out.println("证书颁发者:" + info);
                            // 获得证书签名算法名称
                            info = cert.getSigAlgName();
                            System.out.println("证书签名算法:" + info);

                        }else{
                            System.out.println("握手失败");
                        }
                    }
                });

        SocketChannel channel = (SocketChannel) ctx.channel();
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+" conn:");
        System.out.println("IP:" + channel.localAddress().getHostString());
        System.out.println("Port:" + channel.localAddress().getPort());
    }

4、配置完成后,先启动服务器,再启动客户端,可以看到连接建立成功



猜你喜欢

转载自blog.csdn.net/zhangfls/article/details/108712415