MQTTnet库的MQTT Client证书连接模式

27 篇文章 6 订阅

       MQTT为了加强安全性,是支持Tls的安全模式连接的。在安全连接模式下,默认的服务端口由1883变更为8883,且使用CA证书来保证通讯双方的安全性。

      MQTTX官方开源了一个多种编程语言的客户端连接Demo,其中C#版本的是基于MQTTnet库的,项目地址如下:

https://github.com/emqx/MQTT-Client-Examples/tree/master/mqtt-client-Csharp/MqttNetTLSClient

Client代码如下:

using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;


namespace MqttClient
{
    public static class Client
    {
        public static async Task TlsClientWithCA(string caFile)
        {
            var mqttFactory = new MqttFactory();

            X509Certificate2 cacert = new X509Certificate2(File.ReadAllBytes(caFile));

            using (var mqttClient = mqttFactory.CreateMqttClient())
            {
                var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("192.168.1.139", 8883)
                    .WithCredentials("emqx", "emqx123")
                    .WithTls(
                        new MqttClientOptionsBuilderTlsParameters()
                        {
                            UseTls = true,
                            SslProtocol = System.Security.Authentication.SslProtocols.Tls12,
                            CertificateValidationHandler = (certContext) =>
                            {
                                X509Chain chain = new X509Chain();
                                chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                                chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
                                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
                                chain.ChainPolicy.VerificationTime = DateTime.Now;
                                chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
                                chain.ChainPolicy.CustomTrustStore.Add(cacert);
                                chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;

                                // convert provided X509Certificate to X509Certificate2
                                var x5092 = new X509Certificate2(certContext.Certificate);

                                return chain.Build(x5092);
                            }
                        })
                    .Build();

                mqttClient.ApplicationMessageReceivedAsync += e =>
                {
                    Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
                    Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
                    Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.PayloadSegment)}");
                    Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
                    Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
                    Console.WriteLine();
                   
                    return Task.CompletedTask;
                };

                using (var timeout = new CancellationTokenSource(5000))
                {
                    await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);

                    Console.WriteLine("The MQTT client is connected.");


                }

                var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
               .WithTopicFilter(
                   f =>
                   {
                       f.WithTopic("mqttnet/samples/topic/2");
                   })
               .Build();

                await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);

                Console.WriteLine("MQTT client subscribed to topic.");

                Console.WriteLine("Press enter to exit.");
                Console.ReadLine();
            }
        }

        public static async Task TlsClientWithCert(string caFile, string pfx, string password)
        {
            var mqttFactory = new MqttFactory();

            var cacert = X509Certificate.CreateFromCertFile(caFile);
            cacert = new X509Certificate2(cacert.Export(X509ContentType.SerializedCert));
            var clientCert = new X509Certificate2(pfx, password);
            var newCert = new X509Certificate2(clientCert.Export(X509ContentType.SerializedCert));


            using (var mqttClient = mqttFactory.CreateMqttClient())
            {
                var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("192.168.1.139", 8883)
                    .WithCredentials("emqx", "emqx123")
                    .WithTls(
                        new MqttClientOptionsBuilderTlsParameters()
                        {
                            UseTls = true,
                            SslProtocol = System.Security.Authentication.SslProtocols.Tls12,
                            
                            CertificateValidationHandler = (o) =>
                            {
                                return true;
                            },
                            Certificates = new List<X509Certificate>() {
                            cacert, newCert
                          }
                        })
                    .Build();

                mqttClient.ApplicationMessageReceivedAsync += e =>
                {
                    Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
                    Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
                    Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.PayloadSegment)}");
                    Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
                    Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
                    Console.WriteLine();
                    return Task.CompletedTask;
                };

                using (var timeout = new CancellationTokenSource(5000))
                {
                    await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);

                    Console.WriteLine("The MQTT client is connected.");


                }

                var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
               .WithTopicFilter(
                   f =>
                   {
                       f.WithTopic("mqttnet/samples/topic/2");
                   })
               .Build();

                await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);

                Console.WriteLine("MQTT client subscribed to topic.");

                Console.WriteLine("Press enter to exit.");
                Console.ReadLine();
            }
        }



    }
}

   主程序代码如下:

namespace MqttClient
{
    class Program
    {

        static void Main(string[] args)
        {
            // 自签名证书单向认证
            // MqttClient.Client.TlsClientWithCA("E:/certs/cacert.pem").Wait();

            // 双向认证
            MqttClient.Client.TlsClientWithCert(@"E:/certs/cacert.pem", @"E:/certs/client.pfx","123456").Wait();
        }
    }
}

在证书连接模式下,我们一般可以获取到的是ca.crt、client.crt和client.key,并不能直接得到pem和pfx文件,这时需要转换使用openssl来转换。

   openssl下载地址:

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions (slproweb.com)

在openssl的命令行下,执行两个转换语句,即可实现pem和pfx的转换。

openssl x509 -inform PEM -in ca.crt -outform PEM -out ca.pem
openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt -certfile ca.crt

也可以不调整ca,直接生成pfx

openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt -certfile ca.crt

这时对应的启动语句变成如下形式:

// 双向认证
            MqttClient.Client.TlsClientWithCert(@"E:/certs/ca.crt", @"E:/certs/client.pfx","123456").Wait();

密码“123456”是在生成pfx文件时输入的,具体密码策略由服务器端预定。

这个连接过程,最需要注意的是生成pfx时,使用的-certfile ca.crt与第一个参数一致

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值