From e01707d25e784afd0c97960e5d1cd278b0d8435e Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Sat, 28 Jan 2023 13:08:08 +0100 Subject: [PATCH] Improve samples --- Samples/Program.cs | 19 +- ...t_Samples.cs => Server_ASP_NET_Samples.cs} | 2 +- Samples/Server/Server_TLS_Samples.cs | 75 ++++++++ Source/MQTTnet.Tests/Server/Tls_Tests.cs | 178 +++++++++--------- 4 files changed, 186 insertions(+), 88 deletions(-) rename Samples/Server/{Server_Asp_Net_Samples.cs => Server_ASP_NET_Samples.cs} (98%) create mode 100644 Samples/Server/Server_TLS_Samples.cs diff --git a/Samples/Program.cs b/Samples/Program.cs index 78f5b255..9cf199a2 100644 --- a/Samples/Program.cs +++ b/Samples/Program.cs @@ -4,7 +4,13 @@ using System.Reflection; -Console.WriteLine("Welcome to MQTTnet samples!"); +Console.BackgroundColor = ConsoleColor.White; +Console.BackgroundColor = ConsoleColor.Red; +Console.Title = "Samples - MQTTnet"; +Console.WriteLine("----------------------------------------------"); +Console.WriteLine("- Welcome to MQTTnet samples! -"); +Console.WriteLine("----------------------------------------------"); +Console.ResetColor(); Console.WriteLine(); var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList(); @@ -16,7 +22,11 @@ foreach (var sampleClass in sampleClasses) index++; } +Console.WriteLine(); +Console.ForegroundColor = ConsoleColor.Green; Console.Write("Please choose sample class (press Enter to continue): "); +Console.ResetColor(); + var input = Console.ReadLine(); var selectedIndex = int.Parse(input ?? "0"); var selectedSampleClass = sampleClasses[selectedIndex]; @@ -29,12 +39,19 @@ foreach (var sampleMethod in sampleMethods) index++; } +Console.WriteLine(); +Console.ForegroundColor = ConsoleColor.Green; Console.Write("Please choose sample (press Enter to continue): "); +Console.ResetColor(); + input = Console.ReadLine(); selectedIndex = int.Parse(input ?? "0"); var selectedSampleMethod = sampleMethods[selectedIndex]; +Console.WriteLine(); +Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Executing sample..."); +Console.ResetColor(); Console.WriteLine(); try diff --git a/Samples/Server/Server_Asp_Net_Samples.cs b/Samples/Server/Server_ASP_NET_Samples.cs similarity index 98% rename from Samples/Server/Server_Asp_Net_Samples.cs rename to Samples/Server/Server_ASP_NET_Samples.cs index d085196d..147caf5a 100644 --- a/Samples/Server/Server_Asp_Net_Samples.cs +++ b/Samples/Server/Server_ASP_NET_Samples.cs @@ -17,7 +17,7 @@ using MQTTnet.Server; namespace MQTTnet.Samples.Server; -public static class Server_Asp_Net_Samples +public static class Server_ASP_NET_Samples { public static Task Start_Server_With_WebSockets_Support() { diff --git a/Samples/Server/Server_TLS_Samples.cs b/Samples/Server/Server_TLS_Samples.cs new file mode 100644 index 00000000..177f8f0b --- /dev/null +++ b/Samples/Server/Server_TLS_Samples.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable InconsistentNaming + +using System.Net; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using MQTTnet.Server; + +namespace MQTTnet.Samples.Server; + +public static class Server_TLS_Samples +{ + public static async Task Run_Server_With_Self_Signed_Certificate() + { + /* + * This sample starts a simple MQTT server which will accept any TCP connection. + * It also has an encrypted connection using a self signed TLS certificate. + * + * See sample "Run_Minimal_Server" for more details. + */ + + var mqttFactory = new MqttFactory(); + + // This certificate is self signed so that + var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1"); + + var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build(); + + using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions)) + { + await mqttServer.StartAsync(); + + Console.WriteLine("Press Enter to exit."); + Console.ReadLine(); + + // Stop and dispose the MQTT server if it is no longer needed! + await mqttServer.StopAsync(); + } + } + + static X509Certificate2 CreateSelfSignedCertificate(string oid) + { + var sanBuilder = new SubjectAlternativeNameBuilder(); + sanBuilder.AddIpAddress(IPAddress.Loopback); + sanBuilder.AddIpAddress(IPAddress.IPv6Loopback); + sanBuilder.AddDnsName("localhost"); + + using (var rsa = RSA.Create()) + { + var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); + + certRequest.CertificateExtensions.Add( + new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); + + certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(oid) }, false)); + + certRequest.CertificateExtensions.Add(sanBuilder.Build()); + + using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10))) + { + var pfxCertificate = new X509Certificate2( + certificate.Export(X509ContentType.Pfx), + (string)null!, + X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable); + + return pfxCertificate; + } + } + } +} \ No newline at end of file diff --git a/Source/MQTTnet.Tests/Server/Tls_Tests.cs b/Source/MQTTnet.Tests/Server/Tls_Tests.cs index 1c9effee..5095f9ec 100644 --- a/Source/MQTTnet.Tests/Server/Tls_Tests.cs +++ b/Source/MQTTnet.Tests/Server/Tls_Tests.cs @@ -1,10 +1,4 @@ - #if !WINDOWS_UWP && (NET48_OR_GREATER || NET5_0_OR_GREATER) -using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.Certificates; -using MQTTnet.Client; -using MQTTnet.Server; -using MQTTnet.Tests.Mockups; using System; using System.Linq; using System.Net; @@ -13,61 +7,52 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Certificates; +using MQTTnet.Client; +using MQTTnet.Formatter; +using MQTTnet.Server; +using MQTTnet.Tests.Mockups; namespace MQTTnet.Tests.Server { - [TestClass] - - public class Tls_Tests : BaseTestClass + public sealed class Tls_Tests : BaseTestClass { - - public X509Certificate2 CreateCertificate(string oid) + static X509Certificate2 CreateCertificate(string oid) { var sanBuilder = new SubjectAlternativeNameBuilder(); sanBuilder.AddIpAddress(IPAddress.Loopback); sanBuilder.AddIpAddress(IPAddress.IPv6Loopback); sanBuilder.AddDnsName("localhost"); - var rsa = RSA.Create(); - - var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); - - certRequest.CertificateExtensions.Add( - new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); - - certRequest.CertificateExtensions.Add( - new X509EnhancedKeyUsageExtension( - new OidCollection { new Oid(oid) }, false)); - - certRequest.CertificateExtensions.Add(sanBuilder.Build()); - - var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)); - var pfxCertificate = new X509Certificate2(certificate.Export(X509ContentType.Pfx), (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable); - return pfxCertificate; - } + using (var rsa = RSA.Create()) + { + var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); - private async Task ConnectClientAsync(TestEnvironment testEnvironment, Func certValidator) - { - var clientOptionsBuilder = testEnvironment.Factory.CreateClientOptionsBuilder(); - clientOptionsBuilder - .WithClientId(Guid.NewGuid().ToString()) - .WithTcpServer("localhost", 8883) - .WithTls(tls => + certRequest.CertificateExtensions.Add( + new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); + + certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false)); + + certRequest.CertificateExtensions.Add(sanBuilder.Build()); + + using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10))) { - tls.UseTls = true; - tls.SslProtocol = SslProtocols.Tls12; - tls.CertificateValidationHandler = certValidator; - }); + var pfxCertificate = new X509Certificate2( + certificate.Export(X509ContentType.Pfx), + (string)null, + X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable); - var clientOptions = clientOptionsBuilder.Build(); - return await testEnvironment.ConnectClient(clientOptions); + return pfxCertificate; + } + } } [TestMethod] public async Task Tls_Swap_Test() { - var testEnvironment = CreateTestEnvironment(MQTTnet.Formatter.MqttProtocolVersion.V500); + var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500); var serverOptionsBuilder = testEnvironment.Factory.CreateServerOptionsBuilder(); var firstOid = "1.3.6.1.5.5.7.3.1"; @@ -78,11 +63,7 @@ namespace MQTTnet.Tests.Server CurrentCertificate = CreateCertificate(firstOid) }; - serverOptionsBuilder - .WithoutDefaultEndpoint() - .WithEncryptedEndpoint() - .WithEncryptionSslProtocol(SslProtocols.Tls12) - .WithEncryptionCertificate(certificateProvider); + serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider); var serverOptions = serverOptionsBuilder.Build(); @@ -97,12 +78,14 @@ namespace MQTTnet.Tests.Server }; await server.StartAsync(); - - var firstClient = await ConnectClientAsync(testEnvironment, args => - { - Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType().First().EnhancedKeyUsages[0].Value); - return true; - }); + + var firstClient = await ConnectClientAsync( + testEnvironment, + args => + { + Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType().First().EnhancedKeyUsages[0].Value); + return true; + }); var firstClientReceivedCount = 0; firstClient.ApplicationMessageReceivedAsync += args => @@ -114,27 +97,31 @@ namespace MQTTnet.Tests.Server await firstClient.SubscribeAsync("TestTopic1"); - await firstClient.PublishAsync(new MqttApplicationMessage - { - Topic = "TestTopic1", - Payload = new byte[] { 1, 2, 3, 4} - }); - - await testEnvironment.Server.InjectApplicationMessage(new InjectedMqttApplicationMessage( + await firstClient.PublishAsync( new MqttApplicationMessage { Topic = "TestTopic1", Payload = new byte[] { 1, 2, 3, 4 } - })); + }); + + await testEnvironment.Server.InjectApplicationMessage( + new InjectedMqttApplicationMessage( + new MqttApplicationMessage + { + Topic = "TestTopic1", + Payload = new byte[] { 1, 2, 3, 4 } + })); certificateProvider.CurrentCertificate = CreateCertificate(secondOid); // Validate that the certificate was switched - var secondClient = await ConnectClientAsync(testEnvironment, args => - { - Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType().First().EnhancedKeyUsages[0].Value); - return true; - }); + var secondClient = await ConnectClientAsync( + testEnvironment, + args => + { + Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType().First().EnhancedKeyUsages[0].Value); + return true; + }); var secondClientReceivedCount = 0; secondClient.ApplicationMessageReceivedAsync += args => @@ -146,32 +133,36 @@ namespace MQTTnet.Tests.Server await secondClient.SubscribeAsync("TestTopic2"); - await firstClient.PublishAsync(new MqttApplicationMessage + await firstClient.PublishAsync( + new MqttApplicationMessage { Topic = "TestTopic2", Payload = new byte[] { 1, 2, 3, 4 } }); - await testEnvironment.Server.InjectApplicationMessage(new InjectedMqttApplicationMessage( - new MqttApplicationMessage - { - Topic = "TestTopic2", - Payload = new byte[] { 1, 2, 3, 4 } - })); + await testEnvironment.Server.InjectApplicationMessage( + new InjectedMqttApplicationMessage( + new MqttApplicationMessage + { + Topic = "TestTopic2", + Payload = new byte[] { 1, 2, 3, 4 } + })); // Ensure first client still works - await firstClient.PublishAsync(new MqttApplicationMessage - { - Topic = "TestTopic1", - Payload = new byte[] { 1, 2, 3, 4 } - }); - - await testEnvironment.Server.InjectApplicationMessage(new InjectedMqttApplicationMessage( + await firstClient.PublishAsync( new MqttApplicationMessage { Topic = "TestTopic1", Payload = new byte[] { 1, 2, 3, 4 } - })); + }); + + await testEnvironment.Server.InjectApplicationMessage( + new InjectedMqttApplicationMessage( + new MqttApplicationMessage + { + Topic = "TestTopic1", + Payload = new byte[] { 1, 2, 3, 4 } + })); await Task.Delay(1000); @@ -182,17 +173,32 @@ namespace MQTTnet.Tests.Server await server.StopAsync(); } - - private class CertificateProvider : ICertificateProvider + static async Task ConnectClientAsync(TestEnvironment testEnvironment, Func certValidator) { + var clientOptionsBuilder = testEnvironment.Factory.CreateClientOptionsBuilder(); + clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString()) + .WithTcpServer("localhost", 8883) + .WithTls( + tls => + { + tls.UseTls = true; + tls.SslProtocol = SslProtocols.Tls12; + tls.CertificateValidationHandler = certValidator; + }); + var clientOptions = clientOptionsBuilder.Build(); + return await testEnvironment.ConnectClient(clientOptions); + } + + sealed class CertificateProvider : ICertificateProvider + { public X509Certificate2 CurrentCertificate { get; set; } public X509Certificate2 GetCertificate() - => CurrentCertificate; - + { + return CurrentCertificate; + } } - } } #endif \ No newline at end of file