Add abstraction layer for server certificate.

This commit is contained in:
Christian Kratky
2020-05-26 23:46:43 +02:00
parent e60a732b61
commit c779b394f1
11 changed files with 118 additions and 44 deletions
@@ -0,0 +1,28 @@
using System;
using System.Security.Cryptography.X509Certificates;
namespace MQTTnet.Certificates
{
public class BlobCertificateProvider : ICertificateProvider
{
public BlobCertificateProvider(byte[] blob)
{
Blob = blob ?? throw new ArgumentNullException(nameof(blob));
}
public byte[] Blob { get; }
public string Password { get; set; }
public X509Certificate2 GetCertificate()
{
if (string.IsNullOrEmpty(Password))
{
// Use a different overload when no password is specified. Otherwise the constructor will fail.
return new X509Certificate2(Blob);
}
return new X509Certificate2(Blob, Password);
}
}
}
@@ -0,0 +1,9 @@
using System.Security.Cryptography.X509Certificates;
namespace MQTTnet.Certificates
{
public interface ICertificateProvider
{
X509Certificate2 GetCertificate();
}
}
@@ -0,0 +1,22 @@
#if !WINDOWS_UWP
using System;
using System.Security.Cryptography.X509Certificates;
namespace MQTTnet.Certificates
{
public class X509CertificateProvider : ICertificateProvider
{
readonly X509Certificate2 _certificate;
public X509CertificateProvider(X509Certificate2 certificate)
{
_certificate = certificate ?? throw new ArgumentNullException(nameof(certificate));
}
public X509Certificate2 GetCertificate()
{
return _certificate;
}
}
}
#endif
@@ -44,30 +44,12 @@ namespace MQTTnet.Implementations
if (options.TlsEndpointOptions?.IsEnabled == true)
{
if (options.TlsEndpointOptions.Certificate == null && options.TlsEndpointOptions.X509Certificate == null)
if (options.TlsEndpointOptions.CertificateProvider == null)
{
throw new ArgumentException("TLS certificate is not set.");
}
else if (options.TlsEndpointOptions.Certificate != null && options.TlsEndpointOptions.X509Certificate != null)
{
throw new ArgumentException($"{nameof(MqttServerTlsTcpEndpointOptions.Certificate)} and {nameof(MqttServerTlsTcpEndpointOptions.X509Certificate)} cannot both be set");
}
X509Certificate2 tlsCertificate;
if (options.TlsEndpointOptions.X509Certificate != null)
{
tlsCertificate = options.TlsEndpointOptions.X509Certificate;
}
else if (string.IsNullOrEmpty(options.TlsEndpointOptions.CertificateCredentials?.Password))
{
// Use a different overload when no password is specified. Otherwise the constructor will fail.
tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
}
else
{
tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate, options.TlsEndpointOptions.CertificateCredentials.Password);
}
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
if (!tlsCertificate.HasPrivateKey)
{
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
@@ -76,13 +58,13 @@ namespace MQTTnet.Implementations
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, _cancellationTokenSource.Token);
}
return Task.FromResult(0);
return PlatformAbstractionLayer.CompletedTask;
}
public Task StopAsync()
{
Cleanup();
return Task.FromResult(0);
return PlatformAbstractionLayer.CompletedTask;
}
protected override void Dispose(bool disposing)
@@ -2,6 +2,8 @@
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using MQTTnet.Certificates;
#if !WINDOWS_UWP
using System.Security.Cryptography.X509Certificates;
#endif
@@ -10,7 +12,7 @@ namespace MQTTnet.Server
{
public class MqttServerOptionsBuilder
{
private readonly MqttServerOptions _options = new MqttServerOptions();
readonly MqttServerOptions _options = new MqttServerOptions();
public MqttServerOptionsBuilder WithConnectionBacklog(int value)
{
@@ -85,17 +87,24 @@ namespace MQTTnet.Server
return this;
}
#if !WINDOWS_UWP
public MqttServerOptionsBuilder WithEncryptionCertificate(byte[] value, IMqttServerCertificateCredentials credentials = null)
{
_options.TlsEndpointOptions.Certificate = value;
_options.TlsEndpointOptions.CertificateCredentials = credentials;
if (value == null) throw new ArgumentNullException(nameof(value));
_options.TlsEndpointOptions.CertificateProvider = new BlobCertificateProvider(value)
{
Password = credentials?.Password
};
return this;
}
#if !WINDOWS_UWP
public MqttServerOptionsBuilder WithEncryptionCertificate(X509Certificate2 certificate)
{
_options.TlsEndpointOptions.X509Certificate = certificate;
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
_options.TlsEndpointOptions.CertificateProvider = new X509CertificateProvider(certificate);
return this;
}
#endif
@@ -1,33 +1,56 @@
using System.Net.Security;
using System;
using System.Security.Authentication;
#if !WINDOWS_UWP
using System.Security.Cryptography.X509Certificates;
#endif
using MQTTnet.Certificates;
namespace MQTTnet.Server
{
public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions
{
ICertificateProvider _certificateProvider;
public MqttServerTlsTcpEndpointOptions()
{
Port = 8883;
}
[Obsolete("Please use CertificateProvider with 'BlobCertificateProvider' instead.")]
public byte[] Certificate { get; set; }
#if !WINDOWS_UWP
public X509Certificate2 X509Certificate { get; set; }
#endif
[Obsolete("Please use CertificateProvider with 'BlobCertificateProvider' including password property instead.")]
public IMqttServerCertificateCredentials CertificateCredentials { get; set; }
#if !WINDOWS_UWP
public System.Net.Security.RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
#endif
public ICertificateProvider CertificateProvider
{
get
{
// Backward compatibility only. Gets converted to auto property when
// obsolete properties are removed.
if (_certificateProvider != null)
{
return _certificateProvider;
}
if (Certificate == null)
{
return null;
}
return new BlobCertificateProvider(Certificate)
{
Password = CertificateCredentials?.Password
};
}
set => _certificateProvider = value;
}
public bool ClientCertificateRequired { get; set; }
public bool CheckCertificateRevocation { get; set; }
#if !WINDOWS_UWP
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
#endif
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
}
}