diff --git a/src/Servers/Kestrel/shared/test/TestResources.cs b/src/Servers/Kestrel/shared/test/TestResources.cs index 626922afc197..d335617c7d75 100644 --- a/src/Servers/Kestrel/shared/test/TestResources.cs +++ b/src/Servers/Kestrel/shared/test/TestResources.cs @@ -1,8 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.IO; +using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; +using System.Threading; +using Xunit; namespace Microsoft.AspNetCore.Testing { @@ -13,14 +17,28 @@ public static class TestResources public static string TestCertificatePath { get; } = Path.Combine(_baseDir, "testCert.pfx"); public static string GetCertPath(string name) => Path.Combine(_baseDir, name); - public static X509Certificate2 GetTestCertificate() - { - return new X509Certificate2(TestCertificatePath, "testPassword"); - } + private const int MutexTimeout = 120 * 1000; + private static readonly Mutex importPfxMutex = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + new Mutex(initiallyOwned: false, "Global\\KestrelTests.Certificates.LoadPfxCertificate") : + null; - public static X509Certificate2 GetTestCertificate(string certName) + public static X509Certificate2 GetTestCertificate(string certName = "testCert.pfx") { - return new X509Certificate2(GetCertPath(certName), "testPassword"); + // On Windows, applications should not import PFX files in parallel to avoid a known system-level + // race condition bug in native code which can cause crashes/corruption of the certificate state. + if (importPfxMutex != null) + { + Assert.True(importPfxMutex.WaitOne(MutexTimeout), "Cannot acquire the global certificate mutex."); + } + + try + { + return new X509Certificate2(GetCertPath(certName), "testPassword"); + } + finally + { + importPfxMutex?.ReleaseMutex(); + } } } }