Expose client certificate at MQTT connection validator. Make DualMode optional.

This commit is contained in:
Christian Kratky
2019-06-06 19:31:43 +02:00
parent 2b9b22def6
commit 60c6074953
19 changed files with 116 additions and 30 deletions
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking;
@@ -31,7 +32,7 @@ namespace MQTTnet.Implementations
_bufferSize = _options.BufferSize;
}
public MqttTcpChannel(StreamSocket socket, IMqttServerOptions serverOptions)
public MqttTcpChannel(StreamSocket socket, X509Certificate2 clientCertificate, IMqttServerOptions serverOptions)
{
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
_bufferSize = serverOptions.DefaultEndpointOptions.BufferSize;
@@ -39,6 +40,7 @@ namespace MQTTnet.Implementations
CreateStreams();
IsSecureConnection = socket.Information.ProtectionLevel >= SocketProtectionLevel.Tls12;
ClientCertificate = clientCertificate;
Endpoint = _socket.Information.RemoteAddress + ":" + _socket.Information.RemotePort;
}
@@ -49,6 +51,8 @@ namespace MQTTnet.Implementations
public bool IsSecureConnection { get; }
public X509Certificate2 ClientCertificate { get; }
public async Task ConnectAsync(CancellationToken cancellationToken)
{
if (_socket == null)
@@ -28,18 +28,22 @@ namespace MQTTnet.Implementations
IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true;
}
public MqttTcpChannel(Stream stream, string endpoint)
public MqttTcpChannel(Stream stream, string endpoint, X509Certificate2 clientCertificate)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
IsSecureConnection = stream is SslStream;
Endpoint = endpoint;
IsSecureConnection = stream is SslStream;
ClientCertificate = clientCertificate;
}
public string Endpoint { get; private set; }
public bool IsSecureConnection { get; }
public X509Certificate2 ClientCertificate { get; }
public async Task ConnectAsync(CancellationToken cancellationToken)
{
Socket socket;
@@ -55,9 +59,16 @@ namespace MQTTnet.Implementations
socket.ReceiveBufferSize = _options.BufferSize;
socket.SendBufferSize = _options.BufferSize;
socket.DualMode = _options.DualMode;
socket.NoDelay = _options.NoDelay;
if (_options.DualMode.HasValue)
{
// It is important to avoid setting the flag if no specific value is set by the user
// because on IPv4 only networks the setter will always throw an exception. Regardless
// of the actual value.
socket.DualMode = _options.DualMode.Value;
}
// Workaround for: workaround for https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(() => socket.Dispose()))
{
@@ -1,11 +1,13 @@
#if WINDOWS_UWP
using System;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Server;
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace MQTTnet.Implementations
{
@@ -73,7 +75,21 @@ namespace MQTTnet.Implementations
var clientHandler = ClientHandler;
if (clientHandler != null)
{
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger))
X509Certificate2 clientCertificate = null;
if (args.Socket.Control.ClientCertificate != null)
{
try
{
clientCertificate = new X509Certificate2(args.Socket.Control.ClientCertificate.GetCertificateBlob().ToArray());
}
catch (Exception exception)
{
_logger.Warning(exception, "Unable to convert UWP certificate to X509Certificate2.");
}
}
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(), _logger))
{
await clientHandler(clientAdapter).ConfigureAwait(false);
}
@@ -112,17 +112,27 @@ namespace MQTTnet.Implementations
stream = new NetworkStream(clientSocket, true);
X509Certificate2 clientCertificate = null;
if (_tlsCertificate != null)
{
var sslStream = new SslStream(stream, false);
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
await sslStream.AuthenticateAsServerAsync(
_tlsCertificate,
_tlsOptions.ClientCertificateRequired,
_tlsOptions.SslProtocol,
_tlsOptions.CheckCertificateRevocation).ConfigureAwait(false);
stream = sslStream;
clientCertificate = sslStream.RemoteCertificate as X509Certificate2;
}
var clientHandler = ClientHandler;
if (clientHandler != null)
{
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(stream, remoteEndPoint), new MqttPacketFormatterAdapter(), _logger))
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(stream, remoteEndPoint, clientCertificate), new MqttPacketFormatterAdapter(), _logger))
{
await clientHandler(clientAdapter).ConfigureAwait(false);
}
@@ -11,9 +11,9 @@ namespace MQTTnet.Implementations
{
public class MqttWebSocketChannel : IMqttChannel
{
private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
private readonly MqttClientWebSocketOptions _options;
private SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
private WebSocket _webSocket;
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
@@ -21,18 +21,21 @@ namespace MQTTnet.Implementations
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public MqttWebSocketChannel(WebSocket webSocket, string endpoint, bool isSecureConnection)
public MqttWebSocketChannel(WebSocket webSocket, string endpoint, bool isSecureConnection, X509Certificate2 clientCertificate)
{
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
Endpoint = endpoint;
IsSecureConnection = isSecureConnection;
ClientCertificate = clientCertificate;
}
public string Endpoint { get; }
public bool IsSecureConnection { get; private set; }
public X509Certificate2 ClientCertificate { get; private set; }
public async Task ConnectAsync(CancellationToken cancellationToken)
{
var uri = _options.Uri;
@@ -114,9 +117,14 @@ namespace MQTTnet.Implementations
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// This lock is required because the client will throw an exception if _SendAsync_ is
// The lock is required because the client will throw an exception if _SendAsync_ is
// called from multiple threads at the same time. But this issue only happens with several
// framework versions.
if (_sendLock == null)
{
return;
}
await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
@@ -124,13 +132,14 @@ namespace MQTTnet.Implementations
}
finally
{
_sendLock.Release();
_sendLock?.Release();
}
}
public void Dispose()
{
_sendLock?.Dispose();
_sendLock = null;
try
{