diff --git a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj index 2be6c92a..34f26903 100644 --- a/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj +++ b/src/HASS.Agent/HASS.Agent.Satellite.Service/HASS.Agent.Satellite.Service.csproj @@ -1,7 +1,7 @@ - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 true enable enable diff --git a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj index 90565d05..be60cc9f 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj +++ b/src/HASS.Agent/HASS.Agent.Shared/HASS.Agent.Shared.csproj @@ -1,7 +1,7 @@  - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 x64;x86 HASS.Agent.Shared HASS.Agent.Shared diff --git a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs index b56eace8..903b1358 100644 --- a/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs +++ b/src/HASS.Agent/HASS.Agent/Functions/HelperFunctions.cs @@ -171,7 +171,7 @@ internal static async Task ShutdownAsync(TimeSpan waitBeforeClosing) Variables.MainForm?.HideTrayIcon(); // stop hotkey - Variables.MainForm?.Invoke(new MethodInvoker(delegate + Variables.MainForm?.Invoke(new System.Windows.Forms.MethodInvoker(delegate { Variables.HotKeyListener?.RemoveAll(); Variables.HotKeyListener?.Dispose(); @@ -412,7 +412,7 @@ internal static void LaunchWebView(WebViewInfo webViewInfo, string url = "") if (!string.IsNullOrEmpty(url)) webViewInfo.Url = url; // show it from within the UI thread - Variables.MainForm.Invoke(new MethodInvoker(delegate + Variables.MainForm.Invoke(new System.Windows.Forms.MethodInvoker(delegate { var webView = new WebView(webViewInfo); webView.Opacity = 0; @@ -446,7 +446,7 @@ internal static void PrepareTrayIconWebView() webViewInfo.IsTrayIconWebView = true; // prepare the webview - Variables.MainForm.Invoke(new MethodInvoker(delegate + Variables.MainForm.Invoke(new System.Windows.Forms.MethodInvoker(delegate { // optionally close an existing one Variables.TrayIconWebView?.ForceClose(); @@ -506,7 +506,7 @@ internal static void LaunchTrayIconWebView(WebViewInfo webViewInfo) private static void LaunchTrayIconBackgroundLoadedWebView() { - Variables.MainForm.Invoke(new MethodInvoker(delegate + Variables.MainForm.Invoke(new System.Windows.Forms.MethodInvoker(delegate { // make sure it's ready if (Variables.TrayIconWebView == null || Variables.TrayIconWebView.IsDisposed) @@ -519,7 +519,7 @@ private static void LaunchTrayIconBackgroundLoadedWebView() private static void LaunchTrayIconCustomWebView(WebViewInfo webViewInfo) { - Variables.MainForm.Invoke(new MethodInvoker(delegate + Variables.MainForm.Invoke(new System.Windows.Forms.MethodInvoker(delegate { var x = Screen.PrimaryScreen.WorkingArea.Width - webViewInfo.Width; var y = Screen.PrimaryScreen.WorkingArea.Height - webViewInfo.Height; diff --git a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj index 75aa88d6..169fb80f 100644 --- a/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj +++ b/src/HASS.Agent/HASS.Agent/HASS.Agent.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 disable true true @@ -26,7 +26,7 @@ 2.1.1 HASS.Agent None - win10-x64;win10-x86 + win-x64 true true false diff --git a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs index 0431213b..41752222 100644 --- a/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs +++ b/src/HASS.Agent/HASS.Agent/MQTT/MqttManager.cs @@ -720,21 +720,23 @@ private static ManagedMqttClientOptions GetOptions() if (!string.IsNullOrEmpty(Variables.AppSettings.MqttUsername)) clientOptionsBuilder.WithCredentials(Variables.AppSettings.MqttUsername, Variables.AppSettings.MqttPassword); - var certificates = new List(); + // Load CA cert for server validation + X509Certificate2? caCert = null; if (!string.IsNullOrEmpty(Variables.AppSettings.MqttRootCertificate)) { if (!File.Exists(Variables.AppSettings.MqttRootCertificate)) Log.Error("[MQTT] Provided root certificate not found: {cert}", Variables.AppSettings.MqttRootCertificate); else - certificates.Add(new X509Certificate2(Variables.AppSettings.MqttRootCertificate)); + caCert = new X509Certificate2(Variables.AppSettings.MqttRootCertificate); } + var certificates = new List(); if (!string.IsNullOrEmpty(Variables.AppSettings.MqttClientCertificate)) { if (!File.Exists(Variables.AppSettings.MqttClientCertificate)) Log.Error("[MQTT] Provided client certificate not found: {cert}", Variables.AppSettings.MqttClientCertificate); else - certificates.Add(new X509Certificate2(Variables.AppSettings.MqttClientCertificate)); + certificates.Add(new X509Certificate2(Variables.AppSettings.MqttClientCertificate, "")); // P12, empty password } var clientTlsOptions = new MqttClientTlsOptions() @@ -745,25 +747,50 @@ private static ManagedMqttClientOptions GetOptions() }; //TODO(Amadeo): add more granular control to the UI - if (Variables.AppSettings.MqttAllowUntrustedCertificates) + if (caCert != null) { - clientTlsOptions.IgnoreCertificateChainErrors = Variables.AppSettings.MqttAllowUntrustedCertificates; - clientTlsOptions.IgnoreCertificateRevocationErrors = Variables.AppSettings.MqttAllowUntrustedCertificates; - clientTlsOptions.CertificateValidationHandler = delegate (MqttClientCertificateValidationEventArgs _) + clientTlsOptions.CertificateValidationHandler = ctx => { + var chain = new X509Chain(); + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + + chain.ChainPolicy.TrustMode = X509ChainTrustMode.System; + chain.ChainPolicy.ExtraStore.Add(caCert); + + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; + + var serverCert = new X509Certificate2(ctx.Certificate); + var valid = chain.Build(serverCert); + + if (!valid) + { + Log.Error("[MQTT] Server certificate validation failed. Chain errors: {errors}", + string.Join(", ", chain.ChainStatus + .Where(s => s.Status != X509ChainStatusFlags.NoError) + .Select(s => $"{s.Status}: {s.StatusInformation}"))); + return false; + } return true; }; } + else if (Variables.AppSettings.MqttAllowUntrustedCertificates) + { + clientTlsOptions.CertificateValidationHandler = ctx => true; + } + else + { + clientTlsOptions.CertificateValidationHandler = MqttClientDefaultCertificateValidationHandler.Handle; + } if (certificates.Count > 0) clientTlsOptions.ClientCertificatesProvider = new DefaultMqttCertificatesProvider(certificates); clientOptionsBuilder.WithTlsOptions(clientTlsOptions); - clientOptionsBuilder.Build(); return new ManagedMqttClientOptionsBuilder() .WithAutoReconnectDelay(TimeSpan.FromSeconds(5)) - .WithClientOptions(clientOptionsBuilder).Build(); + .WithClientOptions(clientOptionsBuilder.Build()) + .Build(); } ///