From 32175177fc7a8a03a78bb515fdb1dcefce1b8c9d Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Thu, 5 Feb 2026 22:51:21 +0200 Subject: [PATCH] Fix losing certificates for CredentialSource.Certificate --- .../CertificateLoaderHelper.cs | 13 ++---- .../DefaultCertificateLoader.cs | 45 ++++++++----------- .../DefaultCredentialsLoader.cs | 5 ++- 3 files changed, 25 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.Identity.Web.Certificate/CertificateLoaderHelper.cs b/src/Microsoft.Identity.Web.Certificate/CertificateLoaderHelper.cs index 1b2a1f669..2bd8cc72e 100644 --- a/src/Microsoft.Identity.Web.Certificate/CertificateLoaderHelper.cs +++ b/src/Microsoft.Identity.Web.Certificate/CertificateLoaderHelper.cs @@ -6,15 +6,11 @@ using System.Linq; using System.Security.Cryptography.X509Certificates; using Microsoft.Identity.Abstractions; -using Microsoft.Identity.Web.Diagnostics; namespace Microsoft.Identity.Web { internal sealed class CertificateLoaderHelper { - private static Lazy s_x509KeyStorageFlagsLazy = - new Lazy(DetermineX509KeyStorageFlagLazy); - internal static X509KeyStorageFlags DetermineX509KeyStorageFlag(CredentialDescription credentialDescription) { if (credentialDescription is CertificateDescription credDescription) @@ -29,20 +25,17 @@ internal static X509KeyStorageFlags DetermineX509KeyStorageFlag(CredentialDescri internal static X509KeyStorageFlags DetermineX509KeyStorageFlag() { - return s_x509KeyStorageFlagsLazy.Value; - } - - private static X509KeyStorageFlags DetermineX509KeyStorageFlagLazy() - { #if NET462 || NETSTANDARD2_0 return X509KeyStorageFlags.MachineKeySet; #else +#if NET // This is for app developers using a Mac. MacOS does not support the EphemeralKeySet flag. // See https://learn.microsoft.com/dotnet/standard/security/cross-platform-cryptography#write-a-pkcs12pfx - if (OsHelper.IsMacPlatform()) + if (OperatingSystem.IsMacOS()) { return X509KeyStorageFlags.DefaultKeySet; } +#endif return X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet; #endif diff --git a/src/Microsoft.Identity.Web.Certificate/DefaultCertificateLoader.cs b/src/Microsoft.Identity.Web.Certificate/DefaultCertificateLoader.cs index fd988f2c5..4e00cf887 100644 --- a/src/Microsoft.Identity.Web.Certificate/DefaultCertificateLoader.cs +++ b/src/Microsoft.Identity.Web.Certificate/DefaultCertificateLoader.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.Collections.Generic; -using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -100,13 +98,16 @@ public static string? UserAssignedManagedIdentityClientId public static X509Certificate2? LoadFirstCertificate(IEnumerable certificateDescriptions) { DefaultCertificateLoader defaultCertificateLoader = new(null); - CertificateDescription? certDescription = certificateDescriptions.FirstOrDefault(c => + foreach (var c in certificateDescriptions) { defaultCertificateLoader.LoadCredentialsIfNeededAsync(c).GetAwaiter().GetResult(); - return c.Certificate != null; - }); + if (c.Certificate != null) + { + return c.Certificate; + } + } - return certDescription?.Certificate; + return null; } /// @@ -117,18 +118,16 @@ public static string? UserAssignedManagedIdentityClientId public static async Task LoadFirstCertificateAsync(IEnumerable certificateDescriptions) { DefaultCertificateLoader defaultCertificateLoader = new(null); - CertificateDescription? certDescription = null; foreach (var c in certificateDescriptions) { await defaultCertificateLoader.LoadCredentialsIfNeededAsync(c).ConfigureAwait(false); if (c.Certificate != null) { - certDescription = c; - break; + return c.Certificate; } - }; + } - return certDescription?.Certificate; + return null; } @@ -169,16 +168,7 @@ public static string? UserAssignedManagedIdentityClientId /// /// Description of the certificates. public static void ResetCertificates(IEnumerable? certificateDescriptions) - { - if (certificateDescriptions != null) - { - foreach (var cert in certificateDescriptions) - { - cert.Certificate = null; - cert.CachedValue = null; - } - } - } + => ResetCertificates((IEnumerable?)certificateDescriptions); /// /// Resets all the certificates in the certificate description list. @@ -189,10 +179,13 @@ public static void ResetCertificates(IEnumerable? credent { if (credentialDescription != null) { - foreach (var cert in credentialDescription.Where(c => c.Certificate != null)) + foreach (var cert in credentialDescription) { - cert.Certificate = null; - cert.CachedValue = null; + if (cert.Certificate != null && cert.SourceType != CredentialSource.Certificate) + { + cert.Certificate = null; + cert.CachedValue = null; + } } } } @@ -210,9 +203,9 @@ public void LoadIfNeeded(CertificateDescription certificateDescription) /// Load the certificate from the description, if needed. /// /// Description of the certificate. - public async Task LoadIfNeededAsync(CertificateDescription certificateDescription) + public Task LoadIfNeededAsync(CertificateDescription certificateDescription) { - await LoadCredentialsIfNeededAsync(certificateDescription); + return LoadCredentialsIfNeededAsync(certificateDescription); } } } diff --git a/src/Microsoft.Identity.Web.Certificate/DefaultCredentialsLoader.cs b/src/Microsoft.Identity.Web.Certificate/DefaultCredentialsLoader.cs index 428a5cb84..bdfd69f35 100644 --- a/src/Microsoft.Identity.Web.Certificate/DefaultCredentialsLoader.cs +++ b/src/Microsoft.Identity.Web.Certificate/DefaultCredentialsLoader.cs @@ -79,7 +79,8 @@ public async Task LoadCredentialsIfNeededAsync(CredentialDescription credentialD { _ = Throws.IfNull(credentialDescription); - if (credentialDescription.CachedValue == null) + if (credentialDescription.CachedValue == null + && (credentialDescription.SourceType == CredentialSource.CustomSignedAssertion || CredentialSourceLoaders.ContainsKey(credentialDescription.SourceType))) { // Get or create a semaphore for this credentialDescription var semaphore = _loadingSemaphores.GetOrAdd(credentialDescription.Id, (v) => new SemaphoreSlim(1)); @@ -140,11 +141,11 @@ public void ResetCredentials(IEnumerable credentialDescri { foreach (var credentialDescription in credentialDescriptions) { - credentialDescription.CachedValue = null; credentialDescription.Skip = false; if (credentialDescription.SourceType != CredentialSource.Certificate) { credentialDescription.Certificate = null; + credentialDescription.CachedValue = null; } } }