diff --git a/src/Aspire.Hosting/Dcp/DcpHost.cs b/src/Aspire.Hosting/Dcp/DcpHost.cs index 4a0176ddfe6..c654b785833 100644 --- a/src/Aspire.Hosting/Dcp/DcpHost.cs +++ b/src/Aspire.Hosting/Dcp/DcpHost.cs @@ -143,7 +143,7 @@ internal async Task EnsureDevelopmentCertificateTrustAsync(CancellationToken can try { // Check and warn if the developer certificate is not trusted - if (_developerCertificateService.TrustCertificate && _developerCertificateService.Certificates.Count > 0 && !DeveloperCertificateService.IsCertificateTrusted(_fileSystemService, _developerCertificateService.Certificates.First())) + if (_developerCertificateService.TrustCertificate && _developerCertificateService.Certificates.Count > 0 && !await DeveloperCertificateService.IsCertificateTrustedAsync(_fileSystemService, _developerCertificateService.Certificates.First(), cancellationToken).ConfigureAwait(false)) { var trustLocation = "your project folder"; var appHostDirectory = _configuration["AppHost:Directory"]; diff --git a/src/Aspire.Hosting/DeveloperCertificateService.cs b/src/Aspire.Hosting/DeveloperCertificateService.cs index f30a30637ed..b9c2078e2bd 100644 --- a/src/Aspire.Hosting/DeveloperCertificateService.cs +++ b/src/Aspire.Hosting/DeveloperCertificateService.cs @@ -4,11 +4,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Aspire.Hosting.Dcp.Process; using Aspire.Hosting.Utils; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System.Collections.Immutable; -using System.Diagnostics; using System.Security.Cryptography.X509Certificates; namespace Aspire.Hosting; @@ -112,14 +112,14 @@ public DeveloperCertificateService(ILogger logger, /// public bool UseForHttps { get; } - internal static bool IsCertificateTrusted(IFileSystemService fileSystemService, X509Certificate2 certificate) + internal static async Task IsCertificateTrustedAsync(IFileSystemService fileSystemService, X509Certificate2 certificate, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(certificate); if (OperatingSystem.IsMacOS()) { // On MacOS we have to verify against the Keychain - return IsCertificateTrustedInMacOsKeychain(fileSystemService, certificate); + return await IsCertificateTrustedInMacOsKeychainAsync(fileSystemService, certificate, cancellationToken).ConfigureAwait(false); } try @@ -150,7 +150,7 @@ internal static bool IsCertificateTrusted(IFileSystemService fileSystemService, // Use the same approach as `dotnet dev-certs` to check if the certificate is trusted in the macOS keychain // See: https://github.com/dotnet/aspnetcore/blob/2a88012113497bac5056548f16d810738b069198/src/Shared/CertificateGeneration/MacOSCertificateManager.cs#L36-L37 - private static bool IsCertificateTrustedInMacOsKeychain(IFileSystemService fileSystemService, X509Certificate2 certificate) + private static async Task IsCertificateTrustedInMacOsKeychainAsync(IFileSystemService fileSystemService, X509Certificate2 certificate, CancellationToken cancellationToken) { try { @@ -159,31 +159,18 @@ private static bool IsCertificateTrustedInMacOsKeychain(IFileSystemService fileS File.WriteAllBytes(certPath, certificate.Export(X509ContentType.Cert)); - var startInfo = new ProcessStartInfo + var processSpec = new ProcessSpec("security") { - FileName = "security", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true + Arguments = $"verify-cert -p basic -p ssl -c {certPath}", + ThrowOnNonZeroReturnCode = false }; - startInfo.ArgumentList.Add("verify-cert"); - startInfo.ArgumentList.Add("-p"); - startInfo.ArgumentList.Add("basic"); - startInfo.ArgumentList.Add("-p"); - startInfo.ArgumentList.Add("ssl"); - startInfo.ArgumentList.Add("-c"); - startInfo.ArgumentList.Add(certPath); - - using var process = Process.Start(startInfo); - if (process is null) + var (task, processDisposable) = ProcessUtil.Run(processSpec); + await using (processDisposable.ConfigureAwait(false)) { - return false; + var result = await task.WaitAsync(cancellationToken).ConfigureAwait(false); + return result.ExitCode == 0; } - - process.WaitForExit(); - return process.ExitCode == 0; } catch {