mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-30 18:56:30 +08:00
Expose certificate selection in MQTTnet options
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace MQTTnet.Client
|
||||||
|
{
|
||||||
|
public sealed class MqttClientCertificateSelectionEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public MqttClientCertificateSelectionEventArgs(
|
||||||
|
string targetHost,
|
||||||
|
X509CertificateCollection localCertificates,
|
||||||
|
X509Certificate remoteCertificate,
|
||||||
|
string[] acceptableIssuers,
|
||||||
|
MqttClientTcpOptions tcpOptions)
|
||||||
|
{
|
||||||
|
TargetHost = targetHost;
|
||||||
|
LocalCertificates = localCertificates;
|
||||||
|
RemoveCertificate = remoteCertificate;
|
||||||
|
AcceptableIssuers = acceptableIssuers;
|
||||||
|
TcpOptions = tcpOptions ?? throw new ArgumentNullException(nameof(tcpOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] AcceptableIssuers { get; }
|
||||||
|
|
||||||
|
public X509CertificateCollection LocalCertificates { get; }
|
||||||
|
|
||||||
|
public X509Certificate RemoveCertificate { get; }
|
||||||
|
|
||||||
|
public string TargetHost { get; }
|
||||||
|
|
||||||
|
public MqttClientTcpOptions TcpOptions { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,11 +10,20 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientCertificateValidationEventArgs : EventArgs
|
public sealed class MqttClientCertificateValidationEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public X509Certificate Certificate { get; set; }
|
public MqttClientCertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors, IMqttClientChannelOptions clientOptions)
|
||||||
|
{
|
||||||
|
Certificate = certificate;
|
||||||
|
Chain = chain;
|
||||||
|
ClientOptions = clientOptions;
|
||||||
|
SslPolicyErrors = sslPolicyErrors;
|
||||||
|
}
|
||||||
|
|
||||||
public X509Chain Chain { get; set; }
|
public X509Certificate Certificate { get; }
|
||||||
|
|
||||||
|
public X509Chain Chain { get; }
|
||||||
|
|
||||||
|
public IMqttClientChannelOptions ClientOptions { get; }
|
||||||
|
|
||||||
public IMqttClientChannelOptions ClientOptions { get; set; }
|
|
||||||
#if NET452 || NET461 || NET48
|
#if NET452 || NET461 || NET48
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can be a host string name or an object derived from WebRequest.
|
/// Can be a host string name or an object derived from WebRequest.
|
||||||
@@ -22,6 +31,6 @@ namespace MQTTnet.Client
|
|||||||
public object Sender { get; set; }
|
public object Sender { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public SslPolicyErrors SslPolicyErrors { get; set; }
|
public SslPolicyErrors SslPolicyErrors { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,8 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public Func<MqttClientCertificateValidationEventArgs, bool> CertificateValidationHandler { get; set; }
|
public Func<MqttClientCertificateValidationEventArgs, bool> CertificateValidationHandler { get; set; }
|
||||||
|
|
||||||
|
public Func<MqttClientCertificateSelectionEventArgs, X509Certificate> CertificateSelectionHandler { get; set; }
|
||||||
|
|
||||||
public bool UseTls { get; set; }
|
public bool UseTls { get; set; }
|
||||||
|
|
||||||
public bool IgnoreCertificateRevocationErrors { get; set; }
|
public bool IgnoreCertificateRevocationErrors { get; set; }
|
||||||
|
|||||||
@@ -49,6 +49,17 @@ namespace MQTTnet.Client
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttClientTlsOptionsBuilder WithCertificateSelectionHandler(Func<MqttClientCertificateSelectionEventArgs, X509Certificate> certificateSelectionHandler)
|
||||||
|
{
|
||||||
|
if (certificateSelectionHandler == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(certificateSelectionHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
_tlsOptions.CertificateSelectionHandler = certificateSelectionHandler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientTlsOptionsBuilder WithClientCertificates(IEnumerable<X509Certificate2> certificates)
|
public MqttClientTlsOptionsBuilder WithClientCertificates(IEnumerable<X509Certificate2> certificates)
|
||||||
{
|
{
|
||||||
if (certificates == null)
|
if (certificates == null)
|
||||||
|
|||||||
@@ -109,7 +109,26 @@ namespace MQTTnet.Implementations
|
|||||||
targetHost = dns.Host;
|
targetHost = dns.Host;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
|
||||||
|
SslStream sslStream;
|
||||||
|
if (_tcpOptions.TlsOptions.CertificateSelectionHandler != null)
|
||||||
|
{
|
||||||
|
sslStream = new SslStream(
|
||||||
|
networkStream,
|
||||||
|
false,
|
||||||
|
InternalUserCertificateValidationCallback,
|
||||||
|
InternalUserCertificateSelectionCallback);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use a different constructor depending on the options for MQTTnet so that we do not have
|
||||||
|
// to copy the exact same behavior of the selection handler.
|
||||||
|
sslStream = new SslStream(
|
||||||
|
networkStream,
|
||||||
|
false,
|
||||||
|
InternalUserCertificateValidationCallback);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
#if NETCOREAPP3_1_OR_GREATER
|
||||||
@@ -134,9 +153,7 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
TrustMode = X509ChainTrustMode.CustomRootTrust,
|
TrustMode = X509ChainTrustMode.CustomRootTrust,
|
||||||
VerificationFlags = X509VerificationFlags.IgnoreEndRevocationUnknown,
|
VerificationFlags = X509VerificationFlags.IgnoreEndRevocationUnknown,
|
||||||
RevocationMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors
|
RevocationMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode
|
||||||
? X509RevocationMode.NoCheck
|
|
||||||
: _tcpOptions.TlsOptions.RevocationMode
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sslOptions.CertificateChainPolicy.CustomTrustStore.AddRange(_tcpOptions.TlsOptions.TrustChain);
|
sslOptions.CertificateChainPolicy.CustomTrustStore.AddRange(_tcpOptions.TlsOptions.TrustChain);
|
||||||
@@ -292,19 +309,34 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X509Certificate InternalUserCertificateSelectionCallback(
|
||||||
|
object sender,
|
||||||
|
string targetHost,
|
||||||
|
X509CertificateCollection localCertificates,
|
||||||
|
X509Certificate remoteCertificate,
|
||||||
|
string[] acceptableIssuers)
|
||||||
|
{
|
||||||
|
var certificateSelectionHandler = _tcpOptions?.TlsOptions?.CertificateSelectionHandler;
|
||||||
|
if (certificateSelectionHandler != null)
|
||||||
|
{
|
||||||
|
var eventArgs = new MqttClientCertificateSelectionEventArgs(targetHost, localCertificates, remoteCertificate, acceptableIssuers, _tcpOptions);
|
||||||
|
return certificateSelectionHandler(eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localCertificates?.Count > 0)
|
||||||
|
{
|
||||||
|
return localCertificates[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||||
{
|
{
|
||||||
var certificateValidationHandler = _tcpOptions?.TlsOptions?.CertificateValidationHandler;
|
var certificateValidationHandler = _tcpOptions?.TlsOptions?.CertificateValidationHandler;
|
||||||
if (certificateValidationHandler != null)
|
if (certificateValidationHandler != null)
|
||||||
{
|
{
|
||||||
var eventArgs = new MqttClientCertificateValidationEventArgs
|
var eventArgs = new MqttClientCertificateValidationEventArgs(x509Certificate, chain, sslPolicyErrors, _tcpOptions);
|
||||||
{
|
|
||||||
Certificate = x509Certificate,
|
|
||||||
Chain = chain,
|
|
||||||
SslPolicyErrors = sslPolicyErrors,
|
|
||||||
ClientOptions = _tcpOptions
|
|
||||||
};
|
|
||||||
|
|
||||||
return certificateValidationHandler(eventArgs);
|
return certificateValidationHandler(eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,17 +262,16 @@ namespace MQTTnet.Implementations
|
|||||||
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||||
{
|
{
|
||||||
// TODO: Find a way to add client options to same callback. Problem is that they have a different type.
|
// TODO: Find a way to add client options to same callback. Problem is that they have a different type.
|
||||||
var context = new MqttClientCertificateValidationEventArgs
|
var context = new MqttClientCertificateValidationEventArgs(certificate, chain, sslPolicyErrors, _options);
|
||||||
{
|
|
||||||
Certificate = certificate,
|
|
||||||
Chain = chain,
|
|
||||||
SslPolicyErrors = sslPolicyErrors,
|
|
||||||
ClientOptions = _options
|
|
||||||
};
|
|
||||||
|
|
||||||
return certificateValidationHandler(context);
|
return certificateValidationHandler(context);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
var certificateSelectionHandler = _options.TlsOptions?.CertificateSelectionHandler;
|
||||||
|
if (certificateSelectionHandler != null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Remote certificate selection callback is not supported for WebSocket connections.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user