DotNetty TLS 开启双向认证加密传输数据

DotNetty TLS 开启双向认证加密传输数据

DotNetty为服务间通讯,包括提供服务的服务器端和请求数据的客户端。如果需要密文传输数据则需要开启TLS,用于通讯加密。TLS涉及到的是证书,首先来看看如果生成DotNetty的TLS证书。

一、生成PFX证书

SSL/TLS证书格式(X.509)分为PEM - Privacy Enhanced Mail、DER - Distinguished Encoding Rules

  • PEM
    Apache和Nginx服务器偏向于使用这种编码格式,内容是BASE64编码,"-----BEGIN…"开始, "-----END…"结束;
  • DER
    Java和Windows服务器偏向于使用这种编码格式,二进制格式,不可读(cat打开有惊喜)。window为PFX/P12(pfx/p12),包含公钥和私钥的二进制格式证书,Java为JKS(Java Key Storage)。

Netty使用的是JKS,可以由keytool将pfx证书转为jks,或者keytool生成jks证书;
DotNetty使用的是PFX,用OpenSSL生成,原理和详细说明看上篇OpenSSL 生成pfx,以下节选linux下生成pfx证书脚本:

# 生成私钥
openssl genrsa -aes256 -passout "pass:yangyiquan" -out key.pem 4096
# 生成公钥
openssl req -new -x509 -days 3650 -key key.pem -passin "pass:yangyiquan" -out cert.csr -subj "/C=CN"
# 打包为pfx
openssl pkcs12 -export -in cert.csr -inkey key.pem -out dotnett.linux.pfx

## 输入三次密码:yangyiquan

注:需要将证书拷贝到工程中,服务端、客户端都要,并且在证书属性中将‘生成操作’改为‘内容’,‘复制到输出目录’改为‘如果较新则复制’

二、服务器端

2.1 引用Nuget:

  1. DotNetty.Codecs
  2. DotNetty.Handlers
  3. DotNetty.Transport.Libuv
  4. DotNetty.Handlers
  5. DotNetty.Transport

展示DotNetty项目的演示代码SecureChat.Server,有改动删除了一些依赖:

2.2 创建处理请求类

SecureChatServerHandler.cs

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace SecureChat.Server
{
    using System;
    using System.Net;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Groups;

    public class SecureChatServerHandler : SimpleChannelInboundHandler<string>
    {
        static volatile IChannelGroup group;

        public override void ChannelActive(IChannelHandlerContext contex)
        {
            IChannelGroup g = group;
            //单例 保证只有一个Group
            if (g == null)
            {
                lock (this)
                {
                    if (group == null)
                    {
                        g = group = new DefaultChannelGroup(contex.Executor);
                    }
                }
            }

            contex.WriteAndFlushAsync(string.Format("Welcome to {0} secure chat server!\n", Dns.GetHostName()));
            g.Add(contex.Channel);
        }

        //删了业务类:EveryOneBut
        
		//这里 实际用的时候改为了 ChannelRead,读取请求信息并进行处理
        protected override void ChannelRead0(IChannelHandlerContext contex, string msg)
        {
            //send message to all but this one
            string broadcast = string.Format("[{0}] {1}\n", contex.Channel.RemoteAddress, msg);
            string response = string.Format("[you] {0}\n", msg);
            //处理业务
            contex.WriteAndFlushAsync(response);

            if (string.Equals("bye", msg, StringComparison.OrdinalIgnoreCase))
            {
                contex.CloseAsync();
            }
        }

		//读取请求数据完毕
        public override void ChannelReadComplete(IChannelHandlerContext ctx) => ctx.Flush();

		//当客户端断开时触发该方法,用于关闭连接
        public override void ExceptionCaught(IChannelHandlerContext ctx, Exception e)
        {
            Console.WriteLine("{0}", e.StackTrace);
            ctx.CloseAsync();
        }

        public override bool IsSharable => true;
    }
}

2.3 注册DotNetty监听服务

Program.cs

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace SecureChat.Server
{
    using System;
    using System.IO;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading.Tasks;
    using DotNetty.Codecs;
    using DotNetty.Handlers.Logging;
    using DotNetty.Handlers.Tls;
    using DotNetty.Transport.Bootstrapping;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Sockets;
    //using Examples.Common;

    class Program
    {
        static async Task RunServerAsync()
        {
            ExampleHelper.SetConsoleLogger();

            var bossGroup = new MultithreadEventLoopGroup(1);
            var workerGroup = new MultithreadEventLoopGroup();

            var STRING_ENCODER = new StringEncoder();
            var STRING_DECODER = new StringDecoder();
            var SERVER_HANDLER = new SecureChatServerHandler();

            X509Certificate2 tlsCertificate = null;
            //注释 示例代码的依赖
            //if (ServerSettings.IsSsl)
            //{
                //tlsCertificate = new X509Certificate2(Path.Combine(ExampleHelper.ProcessDirectory, "dotnetty.com.pfx"), "password");
                //改为 dotnetty.linux.pfx证书为第一步生成,yangyiquan证书密码
                tlsCertificate = new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "dotnetty.linux.pfx"), "yangyiquan");
            //}
            try
            {
                var bootstrap = new ServerBootstrap();
                bootstrap
                    .Group(bossGroup, workerGroup)
                    .Channel<TcpServerSocketChannel>()
                    .Option(ChannelOption.SoBacklog, 100)
                    .Handler(new LoggingHandler(LogLevel.INFO))
                    .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;
                        if (tlsCertificate != null)
                        {
                            pipeline.AddLast(TlsHandler.Server(tlsCertificate));
                        }

                        pipeline.AddLast(new DelimiterBasedFrameDecoder(8192, Delimiters.LineDelimiter()));
                        pipeline.AddLast(STRING_ENCODER, STRING_DECODER, SERVER_HANDLER);
                    }));

                IChannel bootstrapChannel = await bootstrap.BindAsync(ServerSettings.Port);

                Console.ReadLine();

                await bootstrapChannel.CloseAsync();
            }
            finally
            {
                Task.WaitAll(bossGroup.ShutdownGracefullyAsync(), workerGroup.ShutdownGracefullyAsync());
            }
        }

        static void Main() => RunServerAsync().Wait();
    }
}

三、客户端端

3.1 引用Nuget

3.2 响应处理

展示DotNetty项目的演示代码SecureChat.Client,有改动删除了一些依赖

客户端发起请求到服务器端,服务器端处理完毕后发送数据到客户端,客户端需要接收并处理。
SecureChatClientHandler.cs

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace SecureChat.Client
{
    using System;
    using DotNetty.Transport.Channels;

    public class SecureChatClientHandler : SimpleChannelInboundHandler<string>
    {
        //接收到数据,并处理业务 注意改为 ChannelRead,
        protected override void ChannelRead0(IChannelHandlerContext contex, string msg) => Console.WriteLine(msg);

		//断开连接会触发该方法
        public override void ExceptionCaught(IChannelHandlerContext contex, Exception e)
        {
            Console.WriteLine(DateTime.Now.Millisecond);
            Console.WriteLine(e.StackTrace);
            contex.CloseAsync();
        }
    }
}

3.3 连接到服务端

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace SecureChat.Client
{
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading.Tasks;
    using DotNetty.Codecs;
    using DotNetty.Handlers.Tls;
    using DotNetty.Transport.Bootstrapping;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Sockets;
//    using Examples.Common;

    class Program
    {
        static async Task RunClientAsync()
        {
            ExampleHelper.SetConsoleLogger();

            var group = new MultithreadEventLoopGroup();

            X509Certificate2 cert = null;
            string targetHost = null;
            //注释 示例代码的依赖
            //if (ClientSettings.IsSsl)
            //{
                //cert = new X509Certificate2(Path.Combine(ExampleHelper.ProcessDirectory, "dotnetty.com.pfx"), "password");
                //改为  dotnetty.linux.pfx证书为第一步生成,yangyiquan证书密码
                cert = new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "dotnetty.linux.pfx"), "yangyiquan");
                targetHost = cert.GetNameInfo(X509NameType.DnsName, false);
            //}
            try
            {
                var bootstrap = new Bootstrap();
                bootstrap
                    .Group(group)
                    .Channel<TcpSocketChannel>()
                    .Option(ChannelOption.TcpNodelay, true)
                    .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;

                        if (cert != null)
                        {
                            pipeline.AddLast(new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(targetHost)));
                        }

                        pipeline.AddLast(new DelimiterBasedFrameDecoder(8192, Delimiters.LineDelimiter()));
                        //注册 接收服务端数据,并处理
                        pipeline.AddLast(new StringEncoder(), new StringDecoder(), new SecureChatClientHandler());
                    }));

                IChannel bootstrapChannel = await bootstrap.ConnectAsync(new IPEndPoint(ClientSettings.Host, ClientSettings.Port));

				//模拟发送消息到服务端
                for (;;)
                {
                    string line = Console.ReadLine();
                    if (string.IsNullOrEmpty(line))
                    {
                        continue;
                    }

                    try
                    {
                    	//发送数据给服务端,即发起一次RPC
                        await bootstrapChannel.WriteAndFlushAsync(line + "\r\n");
                    }
                    catch
                    {
                    }
                    //断开与服务器的连接,并结束当前程序
                    if (string.Equals(line, "bye", StringComparison.OrdinalIgnoreCase))
                    {
                        await bootstrapChannel.CloseAsync();
                        break;
                    }
                }

                await bootstrapChannel.CloseAsync();
            }
            finally
            {
                group.ShutdownGracefullyAsync().Wait(1000);
            }
        }

        static void Main() => RunClientAsync().Wait();
    }
}

以上为DotNetty使用的完整过程,如果不用TLS将相关的代码删除即可。

DotNetty项目地址:
https://github.com/Azure/DotNetty

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值