@@ -17,9 +17,10 @@ | |||||
* [Server] Fixed handling of _Dup_ flag (Thanks to haeberle) | * [Server] Fixed handling of _Dup_ flag (Thanks to haeberle) | ||||
* [Core] Optimized exception handling | * [Core] Optimized exception handling | ||||
* [Core] Mono is now also supported (Thanks to JTrotta) | * [Core] Mono is now also supported (Thanks to JTrotta) | ||||
* [Client] The options are now passed in _ConnectAsync_ (Breaking change!) | |||||
* [Client] The options are now passed in _ConnectAsync_ (Breaking change! Read Wiki for examples) | |||||
* [Core] Trace class renamed to _MqttNetTrace_ (Breaking change!) | * [Core] Trace class renamed to _MqttNetTrace_ (Breaking change!) | ||||
* [Client] Extended certificate validation options (Breaking change!) | * [Client] Extended certificate validation options (Breaking change!) | ||||
* [Client] Added static certificate validation callback (NetFramework, NetStandard) / ignorable certificate errors (UniversalWindows) to _MqttTcpChannel_ | |||||
</releaseNotes> | </releaseNotes> | ||||
<copyright>Copyright Christian Kratky 2016-2017</copyright> | <copyright>Copyright Christian Kratky 2016-2017</copyright> | ||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M</tags> | <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M</tags> | ||||
@@ -46,6 +46,8 @@ namespace MQTTnet.Implementations | |||||
public Stream ReceiveStream { get; private set; } | public Stream ReceiveStream { get; private set; } | ||||
public Stream RawReceiveStream { get; private set; } | public Stream RawReceiveStream { get; private set; } | ||||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; } | |||||
public async Task ConnectAsync() | public async Task ConnectAsync() | ||||
{ | { | ||||
if (_socket == null) | if (_socket == null) | ||||
@@ -57,7 +59,7 @@ namespace MQTTnet.Implementations | |||||
if (_options.TlsOptions.UseTls) | if (_options.TlsOptions.UseTls) | ||||
{ | { | ||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, UserCertificateValidationCallback); | |||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback); | |||||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); | await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); | ||||
} | } | ||||
@@ -98,8 +100,13 @@ namespace MQTTnet.Implementations | |||||
ReceiveStream = new BufferedStream(RawReceiveStream, BufferSize); | ReceiveStream = new BufferedStream(RawReceiveStream, BufferSize); | ||||
} | } | ||||
private bool UserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) | |||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) | |||||
{ | { | ||||
if (CustomCertificateValidationCallback != null) | |||||
{ | |||||
return CustomCertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _options); | |||||
} | |||||
if (sslPolicyErrors == SslPolicyErrors.None) | if (sslPolicyErrors == SslPolicyErrors.None) | ||||
{ | { | ||||
return true; | return true; | ||||
@@ -40,7 +40,9 @@ namespace MQTTnet.Implementations | |||||
public Stream SendStream => ReceiveStream; | public Stream SendStream => ReceiveStream; | ||||
public Stream ReceiveStream { get; private set; } | public Stream ReceiveStream { get; private set; } | ||||
public Stream RawReceiveStream => ReceiveStream; | public Stream RawReceiveStream => ReceiveStream; | ||||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; } | |||||
public async Task ConnectAsync() | public async Task ConnectAsync() | ||||
{ | { | ||||
if (_socket == null) | if (_socket == null) | ||||
@@ -52,7 +54,7 @@ namespace MQTTnet.Implementations | |||||
if (_options.TlsOptions.UseTls) | if (_options.TlsOptions.UseTls) | ||||
{ | { | ||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, UserCertificateValidationCallback); | |||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback); | |||||
ReceiveStream = _sslStream; | ReceiveStream = _sslStream; | ||||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); | await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); | ||||
} | } | ||||
@@ -77,8 +79,13 @@ namespace MQTTnet.Implementations | |||||
_sslStream = null; | _sslStream = null; | ||||
} | } | ||||
private bool UserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) | |||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) | |||||
{ | { | ||||
if (CustomCertificateValidationCallback != null) | |||||
{ | |||||
return CustomCertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _options); | |||||
} | |||||
if (sslPolicyErrors == SslPolicyErrors.None) | if (sslPolicyErrors == SslPolicyErrors.None) | ||||
{ | { | ||||
return true; | return true; | ||||
@@ -1,4 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Runtime.InteropServices.WindowsRuntime; | using System.Runtime.InteropServices.WindowsRuntime; | ||||
@@ -32,6 +33,8 @@ namespace MQTTnet.Implementations | |||||
public Stream ReceiveStream { get; private set; } | public Stream ReceiveStream { get; private set; } | ||||
public Stream RawReceiveStream { get; private set; } | public Stream RawReceiveStream { get; private set; } | ||||
public Func<MqttClientTcpOptions, IEnumerable<ChainValidationResult>> CustomIgnorableServerCertificateErrorsResolver { get; set; } | |||||
public async Task ConnectAsync() | public async Task ConnectAsync() | ||||
{ | { | ||||
if (_socket == null) | if (_socket == null) | ||||
@@ -47,23 +50,11 @@ namespace MQTTnet.Implementations | |||||
{ | { | ||||
_socket.Control.ClientCertificate = LoadCertificate(_options); | _socket.Control.ClientCertificate = LoadCertificate(_options); | ||||
if (_options.TlsOptions.IgnoreCertificateRevocationErrors) | |||||
{ | |||||
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing); | |||||
//_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Revoked); Not supported. | |||||
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure); | |||||
} | |||||
if (_options.TlsOptions.IgnoreCertificateChainErrors) | |||||
foreach (var ignorableChainValidationResult in ResolveIgnorableServerCertificateErrors()) | |||||
{ | { | ||||
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain); | |||||
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult); | |||||
} | } | ||||
if (_options.TlsOptions.AllowUntrustedCertificates) | |||||
{ | |||||
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); | |||||
} | |||||
await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), SocketProtectionLevel.Tls12); | await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), SocketProtectionLevel.Tls12); | ||||
} | } | ||||
@@ -112,5 +103,34 @@ namespace MQTTnet.Implementations | |||||
return new Certificate(options.TlsOptions.Certificates.First().AsBuffer()); | return new Certificate(options.TlsOptions.Certificates.First().AsBuffer()); | ||||
} | } | ||||
private IEnumerable<ChainValidationResult> ResolveIgnorableServerCertificateErrors() | |||||
{ | |||||
if (CustomIgnorableServerCertificateErrorsResolver != null) | |||||
{ | |||||
return CustomIgnorableServerCertificateErrorsResolver(_options); | |||||
} | |||||
var result = new List<ChainValidationResult>(); | |||||
if (_options.TlsOptions.IgnoreCertificateRevocationErrors) | |||||
{ | |||||
result.Add(ChainValidationResult.RevocationInformationMissing); | |||||
//_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Revoked); Not supported. | |||||
result.Add(ChainValidationResult.RevocationFailure); | |||||
} | |||||
if (_options.TlsOptions.IgnoreCertificateChainErrors) | |||||
{ | |||||
result.Add(ChainValidationResult.IncompleteChain); | |||||
} | |||||
if (_options.TlsOptions.AllowUntrustedCertificates) | |||||
{ | |||||
result.Add(ChainValidationResult.Untrusted); | |||||
} | |||||
return result; | |||||
} | |||||
} | } | ||||
} | } |
@@ -183,6 +183,7 @@ namespace MQTTnet.Core.Tests | |||||
var c1 = await serverAdapter.ConnectTestClient(s, "c1"); | var c1 = await serverAdapter.ConnectTestClient(s, "c1"); | ||||
await c1.PublishAsync(new MqttApplicationMessage("retained", new byte[3], MqttQualityOfServiceLevel.AtLeastOnce, true)); | await c1.PublishAsync(new MqttApplicationMessage("retained", new byte[3], MqttQualityOfServiceLevel.AtLeastOnce, true)); | ||||
await c1.PublishAsync(new MqttApplicationMessage("retained", new byte[0], MqttQualityOfServiceLevel.AtLeastOnce, true)); | |||||
await c1.DisconnectAsync(); | await c1.DisconnectAsync(); | ||||
var c2 = await serverAdapter.ConnectTestClient(s, "c2"); | var c2 = await serverAdapter.ConnectTestClient(s, "c2"); | ||||
@@ -194,7 +195,7 @@ namespace MQTTnet.Core.Tests | |||||
await s.StopAsync(); | await s.StopAsync(); | ||||
Assert.AreEqual(1, receivedMessagesCount); | |||||
Assert.AreEqual(0, receivedMessagesCount); | |||||
} | } | ||||
[TestMethod] | [TestMethod] | ||||