Skip to content

Commit

Permalink
Reduce number of PFX loads in System.Net tests (dotnet/corefx#40821)
Browse files Browse the repository at this point in the history
* Reduce number of PFX loads in System.Net tests

* Address PR feedback


Commit migrated from dotnet/corefx@838832e
  • Loading branch information
stephentoub authored Sep 5, 2019
1 parent 8f91061 commit c5cfe6b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 71 deletions.
107 changes: 43 additions & 64 deletions src/libraries/Common/tests/System/Net/Configuration.Certificates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Xunit;
Expand All @@ -18,78 +16,59 @@ public static partial class Certificates
{
private const string CertificatePassword = "testcertificate";
private const string TestDataFolder = "TestData";
private const int MutexTimeoutMs = 120_000;

private const int MutexTimeout = 120 * 1000;
private static readonly Mutex s_importPfxMutex;
private static readonly X509Certificate2 s_serverCertificate;
private static readonly X509Certificate2 s_clientCertificate;
private static readonly X509Certificate2 s_noEKUCertificate;
private static readonly X509Certificate2 s_selfSignedServerCertificate;
private static readonly X509Certificate2 s_selfSignedClientCertificate;

static Certificates()
{
if (PlatformDetection.IsUap)
Mutex mutex =
PlatformDetection.IsUap ? new Mutex(initiallyOwned: false, "Local\\CoreFXTest.Configuration.Certificates.LoadPfxCertificate") : // UWP doesn't support Global mutexes
PlatformDetection.IsWindows ? new Mutex(initiallyOwned: false, "Global\\CoreFXTest.Configuration.Certificates.LoadPfxCertificate") :
null;
using (mutex)
{
// UWP doesn't support Global mutexes.
s_importPfxMutex = new Mutex(false, "Local\\CoreFXTest.Configuration.Certificates.LoadPfxCertificate");
}
else if (PlatformDetection.IsWindows)
{
s_importPfxMutex = new Mutex(false, "Global\\CoreFXTest.Configuration.Certificates.LoadPfxCertificate");
}
}

public static X509Certificate2 GetServerCertificate() => GetCertificate("testservereku.contoso.com.pfx");

public static X509Certificate2 GetClientCertificate() => GetCertificate("testclienteku.contoso.com.pfx");

public static X509Certificate2 GetNoEKUCertificate() => GetCertificate("testnoeku.contoso.com.pfx");

public static X509Certificate2 GetSelfSignedServerCertificate() => GetCertificate("testselfsignedservereku.contoso.com.pfx");

public static X509Certificate2 GetSelfSignedClientCertificate() => GetCertificate("testselfsignedclienteku.contoso.com.pfx");

public static X509Certificate2Collection GetServerCertificateCollection()
{
var certs = new X509Certificate2Collection();
certs.Add(GetServerCertificate());

return certs;
}

public static X509Certificate2Collection GetClientCertificateCollection()
{
var certs = new X509Certificate2Collection();
certs.Add(GetClientCertificate());

return certs;
}

private static X509Certificate2 GetCertificate(string certificateFileName)
{
// 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 (PlatformDetection.IsWindows)
{
Assert.True(s_importPfxMutex.WaitOne(MutexTimeout), "Cannot acquire the global certificate mutex.");
}
try
{
byte[] serverCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testservereku.contoso.com.pfx"));
byte[] clientCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testclienteku.contoso.com.pfx"));
byte[] noEKUCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testnoeku.contoso.com.pfx"));
byte[] selfSignedServerCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testselfsignedservereku.contoso.com.pfx"));
byte[] selfSignedClientCertificateBytes = File.ReadAllBytes(Path.Combine(TestDataFolder, "testselfsignedclienteku.contoso.com.pfx"));

try
{
return new X509Certificate2(
File.ReadAllBytes(Path.Combine(TestDataFolder, certificateFileName)),
CertificatePassword,
X509KeyStorageFlags.DefaultKeySet | X509KeyStorageFlags.Exportable);
}
catch (Exception ex)
{
Debug.Fail(nameof(Configuration.Certificates.GetCertificate) + " threw " + ex.ToString());
throw;
}
finally
{
if (PlatformDetection.IsWindows)
// 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.
Assert.True(mutex?.WaitOne(MutexTimeoutMs) ?? true, "Could not acquire the global certificate mutex.");
try
{
s_serverCertificate = new X509Certificate2(serverCertificateBytes, CertificatePassword, X509KeyStorageFlags.Exportable);
s_clientCertificate = new X509Certificate2(clientCertificateBytes, CertificatePassword, X509KeyStorageFlags.Exportable);
s_noEKUCertificate = new X509Certificate2(noEKUCertificateBytes, CertificatePassword, X509KeyStorageFlags.Exportable);
s_selfSignedServerCertificate = new X509Certificate2(selfSignedServerCertificateBytes, CertificatePassword, X509KeyStorageFlags.Exportable);
s_selfSignedClientCertificate = new X509Certificate2(selfSignedClientCertificateBytes, CertificatePassword, X509KeyStorageFlags.Exportable);
}
finally { mutex?.ReleaseMutex(); }
}
catch (Exception ex)
{
s_importPfxMutex.ReleaseMutex();
Trace.Fail(nameof(Certificates) + " cctor threw " + ex.ToString());
throw;
}
}
}

// These Get* methods make a copy of the certificates so that consumers own the lifetime of the
// certificates handed back. Consumers are expected to dispose of their certs when done with them.

public static X509Certificate2 GetServerCertificate() => new X509Certificate2(s_serverCertificate);
public static X509Certificate2 GetClientCertificate() => new X509Certificate2(s_clientCertificate);
public static X509Certificate2 GetNoEKUCertificate() => new X509Certificate2(s_noEKUCertificate);
public static X509Certificate2 GetSelfSignedServerCertificate() => new X509Certificate2(s_selfSignedServerCertificate);
public static X509Certificate2 GetSelfSignedClientCertificate() => new X509Certificate2(s_selfSignedClientCertificate);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,21 @@ public class CertificateValidationClientServer : IDisposable
{
private readonly ITestOutputHelper _output;
private readonly X509Certificate2 _clientCertificate;
private readonly X509Certificate2Collection _clientCertificateCollection;
private readonly X509Certificate2 _serverCertificate;
private readonly X509Certificate2Collection _serverCertificateCollection;
private bool _clientCertificateRemovedByFilter;

public CertificateValidationClientServer(ITestOutputHelper output)
{
_output = output;

_serverCertificateCollection = Configuration.Certificates.GetServerCertificateCollection();
_serverCertificate = Configuration.Certificates.GetServerCertificate();

_clientCertificateCollection = Configuration.Certificates.GetClientCertificateCollection();
_clientCertificate = Configuration.Certificates.GetClientCertificate();
}

public void Dispose()
{
_serverCertificate.Dispose();
_clientCertificate.Dispose();
foreach (X509Certificate2 cert in _serverCertificateCollection) cert.Dispose();
foreach (X509Certificate2 cert in _clientCertificateCollection) cert.Dispose();
}

[Theory]
Expand Down

0 comments on commit c5cfe6b

Please sign in to comment.