From 9bca4aa53f27d70c1e0335f56e13623ae08cd2eb Mon Sep 17 00:00:00 2001 From: Christian Kratky Date: Sat, 7 Oct 2017 19:37:51 +0200 Subject: [PATCH] Extended certificate validation options --- .../Implementations/MqttTcpChannel.cs | 25 ++++++++++++++++--- .../Implementations/MqttTcpChannel.cs | 25 ++++++++++++++++--- .../Implementations/MqttTcpChannel.cs | 10 ++++++-- MQTTnet.Core/Client/MqttClientTlsOptions.cs | 4 ++- .../MainPage.xaml.cs | 3 +++ 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/Frameworks/MQTTnet.NetFramework/Implementations/MqttTcpChannel.cs b/Frameworks/MQTTnet.NetFramework/Implementations/MqttTcpChannel.cs index 0ca1b5d..7f32de8 100644 --- a/Frameworks/MQTTnet.NetFramework/Implementations/MqttTcpChannel.cs +++ b/Frameworks/MQTTnet.NetFramework/Implementations/MqttTcpChannel.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using MQTTnet.Core.Channel; using MQTTnet.Core.Client; using System.IO; +using System.Linq; namespace MQTTnet.Implementations { @@ -57,7 +58,7 @@ namespace MQTTnet.Implementations if (_options.TlsOptions.UseTls) { _sslStream = new SslStream(new NetworkStream(_socket, true), false, UserCertificateValidationCallback); - await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.CheckCertificateRevocation).ConfigureAwait(false); + await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); } CreateStreams(_socket, _sslStream); @@ -99,12 +100,28 @@ namespace MQTTnet.Implementations private bool UserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0) + if (sslPolicyErrors == SslPolicyErrors.None) { - return _options.TlsOptions.IgnoreCertificateChainErrors; + return true; } - return false; + if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown)) + { + if (!_options.TlsOptions.IgnoreCertificateRevocationErrors) + { + return false; + } + } + + if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain)) + { + if (!_options.TlsOptions.IgnoreCertificateChainErrors) + { + return false; + } + } + + return _options.TlsOptions.AllowUntrustedCertificates; } private static X509CertificateCollection LoadCertificates(MqttClientOptions options) diff --git a/Frameworks/MQTTnet.NetStandard/Implementations/MqttTcpChannel.cs b/Frameworks/MQTTnet.NetStandard/Implementations/MqttTcpChannel.cs index aa91b12..db08751 100644 --- a/Frameworks/MQTTnet.NetStandard/Implementations/MqttTcpChannel.cs +++ b/Frameworks/MQTTnet.NetStandard/Implementations/MqttTcpChannel.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using MQTTnet.Core.Channel; using MQTTnet.Core.Client; using System.IO; +using System.Linq; namespace MQTTnet.Implementations { @@ -53,7 +54,7 @@ namespace MQTTnet.Implementations { _sslStream = new SslStream(new NetworkStream(_socket, true), false, UserCertificateValidationCallback); ReceiveStream = _sslStream; - await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.CheckCertificateRevocation).ConfigureAwait(false); + await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); } else { @@ -78,12 +79,28 @@ namespace MQTTnet.Implementations private bool UserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0) + if (sslPolicyErrors == SslPolicyErrors.None) { - return _options.TlsOptions.IgnoreCertificateChainErrors; + return true; } - return false; + if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown)) + { + if (!_options.TlsOptions.IgnoreCertificateRevocationErrors) + { + return false; + } + } + + if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain)) + { + if (!_options.TlsOptions.IgnoreCertificateChainErrors) + { + return false; + } + } + + return _options.TlsOptions.AllowUntrustedCertificates; } private static X509CertificateCollection LoadCertificates(MqttClientOptions options) diff --git a/Frameworks/MQTTnet.UniversalWindows/Implementations/MqttTcpChannel.cs b/Frameworks/MQTTnet.UniversalWindows/Implementations/MqttTcpChannel.cs index c7501f0..228865d 100644 --- a/Frameworks/MQTTnet.UniversalWindows/Implementations/MqttTcpChannel.cs +++ b/Frameworks/MQTTnet.UniversalWindows/Implementations/MqttTcpChannel.cs @@ -47,10 +47,11 @@ namespace MQTTnet.Implementations { _socket.Control.ClientCertificate = LoadCertificate(_options); - if (!_options.TlsOptions.CheckCertificateRevocation) + if (_options.TlsOptions.IgnoreCertificateRevocationErrors) { _socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing); - _socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Revoked); + //_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Revoked); Not supported. + _socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure); } if (_options.TlsOptions.IgnoreCertificateChainErrors) @@ -58,6 +59,11 @@ namespace MQTTnet.Implementations _socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain); } + if (_options.TlsOptions.AllowUntrustedCertificates) + { + _socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); + } + await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), SocketProtectionLevel.Tls12); } diff --git a/MQTTnet.Core/Client/MqttClientTlsOptions.cs b/MQTTnet.Core/Client/MqttClientTlsOptions.cs index a5595d2..6fc4ab9 100644 --- a/MQTTnet.Core/Client/MqttClientTlsOptions.cs +++ b/MQTTnet.Core/Client/MqttClientTlsOptions.cs @@ -6,10 +6,12 @@ namespace MQTTnet.Core.Client { public bool UseTls { get; set; } - public bool CheckCertificateRevocation { get; set; } + public bool IgnoreCertificateRevocationErrors { get; set; } public bool IgnoreCertificateChainErrors { get; set; } + public bool AllowUntrustedCertificates { get; set; } + public List Certificates { get; set; } } } diff --git a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs index 0ad499e..1014e02 100644 --- a/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs +++ b/Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs @@ -63,6 +63,9 @@ namespace MQTTnet.TestApp.UniversalWindows options.Password = Password.Text; options.ClientId = ClientId.Text; options.TlsOptions.UseTls = UseTls.IsChecked == true; + options.TlsOptions.IgnoreCertificateChainErrors = true; + options.TlsOptions.IgnoreCertificateRevocationErrors = true; + options.TlsOptions.AllowUntrustedCertificates = true; try {