@@ -17,9 +17,10 @@ | |||
* [Server] Fixed handling of _Dup_ flag (Thanks to haeberle) | |||
* [Core] Optimized exception handling | |||
* [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!) | |||
* [Client] Extended certificate validation options (Breaking change!) | |||
* [Client] Added static certificate validation callback (NetFramework, NetStandard) / ignorable certificate errors (UniversalWindows) to _MqttTcpChannel_ | |||
</releaseNotes> | |||
<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> | |||
@@ -46,6 +46,8 @@ namespace MQTTnet.Implementations | |||
public Stream ReceiveStream { get; private set; } | |||
public Stream RawReceiveStream { get; private set; } | |||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; } | |||
public async Task ConnectAsync() | |||
{ | |||
if (_socket == null) | |||
@@ -57,7 +59,7 @@ namespace MQTTnet.Implementations | |||
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); | |||
} | |||
@@ -98,8 +100,13 @@ namespace MQTTnet.Implementations | |||
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) | |||
{ | |||
return true; | |||
@@ -40,7 +40,9 @@ namespace MQTTnet.Implementations | |||
public Stream SendStream => ReceiveStream; | |||
public Stream ReceiveStream { get; private set; } | |||
public Stream RawReceiveStream => ReceiveStream; | |||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; } | |||
public async Task ConnectAsync() | |||
{ | |||
if (_socket == null) | |||
@@ -52,7 +54,7 @@ namespace MQTTnet.Implementations | |||
if (_options.TlsOptions.UseTls) | |||
{ | |||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, UserCertificateValidationCallback); | |||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback); | |||
ReceiveStream = _sslStream; | |||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); | |||
} | |||
@@ -77,8 +79,13 @@ namespace MQTTnet.Implementations | |||
_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) | |||
{ | |||
return true; | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Runtime.InteropServices.WindowsRuntime; | |||
@@ -32,6 +33,8 @@ namespace MQTTnet.Implementations | |||
public Stream ReceiveStream { get; private set; } | |||
public Stream RawReceiveStream { get; private set; } | |||
public Func<MqttClientTcpOptions, IEnumerable<ChainValidationResult>> CustomIgnorableServerCertificateErrorsResolver { get; set; } | |||
public async Task ConnectAsync() | |||
{ | |||
if (_socket == null) | |||
@@ -47,23 +50,11 @@ namespace MQTTnet.Implementations | |||
{ | |||
_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); | |||
} | |||
@@ -112,5 +103,34 @@ namespace MQTTnet.Implementations | |||
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"); | |||
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(); | |||
var c2 = await serverAdapter.ConnectTestClient(s, "c2"); | |||
@@ -194,7 +195,7 @@ namespace MQTTnet.Core.Tests | |||
await s.StopAsync(); | |||
Assert.AreEqual(1, receivedMessagesCount); | |||
Assert.AreEqual(0, receivedMessagesCount); | |||
} | |||
[TestMethod] | |||