From 7be55ce92250d2f0c2753034427f187dd26a8ea1 Mon Sep 17 00:00:00 2001 From: Suhas Holla Karkada Chandrashekar <51073384+suhashollakc@users.noreply.github.com> Date: Fri, 3 Jul 2026 10:56:41 -0500 Subject: [PATCH] Fix default TLS certificate validation (BREAKING CHANGE) (#2254) Fix default TLS certificate validation --- Source/MQTTnet.Tests/MqttTcpChannel_Tests.cs | 28 ++++++++++++++++++- .../MQTTnet/Implementations/MqttTcpChannel.cs | 11 ++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Source/MQTTnet.Tests/MqttTcpChannel_Tests.cs b/Source/MQTTnet.Tests/MqttTcpChannel_Tests.cs index a0742425..8b00db87 100644 --- a/Source/MQTTnet.Tests/MqttTcpChannel_Tests.cs +++ b/Source/MQTTnet.Tests/MqttTcpChannel_Tests.cs @@ -3,7 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Net; +using System.Net.Security; using System.Net.Sockets; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; using MQTTnet.Implementations; namespace MQTTnet.Tests; @@ -12,6 +15,21 @@ namespace MQTTnet.Tests; [TestClass] public class MqttTcpChannel_Tests { + [TestMethod] + public void Certificate_Validation_Uses_Default_Handler_When_No_Custom_Handler() + { + var options = new MqttClientOptionsBuilder().WithTcpServer("localhost") + .WithTlsOptions(o => o.WithAllowUntrustedCertificates().WithIgnoreCertificateRevocationErrors()) + .Build(); + + var tcpChannel = new MqttTcpChannel(options); + + using var chain = new X509Chain(); + var isValid = InvokeCertificateValidationCallback(tcpChannel, chain, SslPolicyErrors.RemoteCertificateChainErrors); + + Assert.IsTrue(isValid); + } + [TestMethod] public async Task Dispose_Channel_While_Used() { @@ -77,4 +95,12 @@ public class MqttTcpChannel_Tests ct.Cancel(false); } } -} \ No newline at end of file + + static bool InvokeCertificateValidationCallback(MqttTcpChannel tcpChannel, X509Chain chain, SslPolicyErrors sslPolicyErrors) + { + var method = typeof(MqttTcpChannel).GetMethod("InternalUserCertificateValidationCallback", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(method); + + return (bool)method.Invoke(tcpChannel, [null, null, chain, sslPolicyErrors]); + } +} diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs index 4fa293ea..82911000 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs @@ -362,23 +362,18 @@ public sealed class MqttTcpChannel : IMqttChannel bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { + var eventArgs = new MqttClientCertificateValidationEventArgs(x509Certificate, chain, sslPolicyErrors, _tcpOptions); var certificateValidationHandler = _tcpOptions?.TlsOptions?.CertificateValidationHandler; if (certificateValidationHandler != null) { - var eventArgs = new MqttClientCertificateValidationEventArgs(x509Certificate, chain, sslPolicyErrors, _tcpOptions); return certificateValidationHandler(eventArgs); } - if (_tcpOptions?.TlsOptions?.IgnoreCertificateChainErrors ?? false) - { - sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors; - } - - return sslPolicyErrors == SslPolicyErrors.None; + return MqttClientDefaultCertificateValidationHandler.Handle(eventArgs); } X509CertificateCollection LoadCertificates() { return _tcpOptions.TlsOptions.ClientCertificatesProvider?.GetCertificates(); } -} \ No newline at end of file +}