Просмотр исходного кода

Make Kestrel prefer on-disk cert for macOS bind. (#44835)

Aditya Mandaleeka 3 лет назад
Родитель
Сommit
d417c3a9bd

+ 1 - 1
src/Servers/Kestrel/Core/src/KestrelServerOptions.cs

@@ -287,7 +287,7 @@ public class KestrelServerOptions
             var logger = ApplicationServices!.GetRequiredService<ILogger<KestrelServer>>();
             try
             {
-                DefaultCertificate = CertificateManager.Instance.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true)
+                DefaultCertificate = CertificateManager.Instance.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true, requireExportable: false)
                     .FirstOrDefault();
 
                 if (DefaultCertificate != null)

+ 2 - 2
src/Shared/CertificateGeneration/CertificateManager.cs

@@ -78,7 +78,7 @@ internal abstract class CertificateManager
         {
             using var store = new X509Store(storeName, location);
             store.Open(OpenFlags.ReadOnly);
-            PopulateCertificatesFromStore(store, certificates);
+            PopulateCertificatesFromStore(store, certificates, requireExportable);
             IEnumerable<X509Certificate2> matchingCertificates = certificates;
             matchingCertificates = matchingCertificates
                 .Where(c => HasOid(c, AspNetHttpsOid));
@@ -161,7 +161,7 @@ internal abstract class CertificateManager
             GetCertificateVersion(certificate) >= AspNetHttpsCertificateVersion;
     }
 
-    protected virtual void PopulateCertificatesFromStore(X509Store store, List<X509Certificate2> certificates)
+    protected virtual void PopulateCertificatesFromStore(X509Store store, List<X509Certificate2> certificates, bool requireExportable)
     {
         certificates.AddRange(store.Certificates.OfType<X509Certificate2>());
     }

+ 10 - 7
src/Shared/CertificateGeneration/MacOSCertificateManager.cs

@@ -373,14 +373,14 @@ internal sealed class MacOSCertificateManager : CertificateManager
         return ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: false);
     }
 
-    protected override void PopulateCertificatesFromStore(X509Store store, List<X509Certificate2> certificates)
+    protected override void PopulateCertificatesFromStore(X509Store store, List<X509Certificate2> certificates, bool requireExportable)
     {
         if (store.Name! == StoreName.My.ToString() && store.Location == StoreLocation.CurrentUser && Directory.Exists(MacOSUserHttpsCertificateLocation))
         {
             var certsFromDisk = GetCertsFromDisk();
 
             var certsFromStore = new List<X509Certificate2>();
-            base.PopulateCertificatesFromStore(store, certsFromStore);
+            base.PopulateCertificatesFromStore(store, certsFromStore, requireExportable);
 
             // Certs created by pre-.NET 7.
             var onlyOnKeychain = certsFromStore.Except(certsFromDisk, ThumbprintComparer.Instance);
@@ -388,10 +388,13 @@ internal sealed class MacOSCertificateManager : CertificateManager
             // Certs created (or "upgraded") by .NET 7+.
             // .NET 7+ installs the certificate on disk as well as on the user keychain (for backwards
             // compatibility with pre-.NET 7).
-            // Note that the actual certs we populate need to be the ones from the store location, and
-            // not the version from disk, since we may do other operations with these certs later (such
-            // as exporting) which would fail with crypto errors otherwise.
-            var onDiskAndKeychain = certsFromStore.Intersect(certsFromDisk, ThumbprintComparer.Instance);
+            // Note that if we require exportable certs, the actual certs we populate need to be the ones
+            // from the store location, and not the version from disk. If we don't require exportability,
+            // we favor the version of the cert that's on disk (avoiding unnecessary keychain access
+            // prompts). Intersect compares with the specified comparer and returns the matching elements
+            // from the first set.
+            var onDiskAndKeychain = requireExportable ? certsFromStore.Intersect(certsFromDisk, ThumbprintComparer.Instance)
+                                                      : certsFromDisk.Intersect(certsFromStore, ThumbprintComparer.Instance);
 
             // The only times we can find a certificate on the keychain and a certificate on keychain+disk
             // are when the certificate on disk and keychain has expired and a pre-.NET 7 SDK has been
@@ -403,7 +406,7 @@ internal sealed class MacOSCertificateManager : CertificateManager
         }
         else
         {
-            base.PopulateCertificatesFromStore(store, certificates);
+            base.PopulateCertificatesFromStore(store, certificates, requireExportable);
         }
     }