Skip to content

Commit 48d40e0

Browse files
authored
Add mitigations to HttpsClientCert_GetCertInformation flakiness (#1529)
1 parent 8f99140 commit 48d40e0

File tree

3 files changed

+83
-61
lines changed

3 files changed

+83
-61
lines changed

samples/NativeIISSample/Properties/launchSettings.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"commandLineArgs": "$(IISExpressArguments)",
1515
"environmentVariables": {
1616
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
17-
"ANCM_PATH": "$(AncmPath)",
18-
"ANCMV2_PATH": "$(AncmV2Path)",
17+
"ANCM_PATH": "$(AspNetCoreModuleV1ShimDll)",
18+
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
1919
"ANCM_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
2020
"LAUNCHER_ARGS": "$(TargetPath)",
2121
"ASPNETCORE_ENVIRONMENT": "Development",
@@ -29,9 +29,9 @@
2929
"commandLineArgs": "$(IISArguments)",
3030
"environmentVariables": {
3131
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
32-
"ANCM_PATH": "$(AncmPath)",
33-
"ANCMV2_PATH": "$(AncmV2Path)",
34-
"ANCM_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
32+
"ANCM_PATH": "$(AspNetCoreModuleV1ShimDll)",
33+
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
34+
"ASPNETCORE_MODULE_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
3535
"LAUNCHER_ARGS": "$(TargetPath)",
3636
"ASPNETCORE_ENVIRONMENT": "Development",
3737
"LAUNCHER_PATH": "$(DotNetPath)",

test/Common.FunctionalTests/ClientCertificateFixture.cs

Lines changed: 48 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,68 +11,57 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
1111
public class ClientCertificateFixture : IDisposable
1212
{
1313
private X509Certificate2 _certificate;
14+
private const string _certIssuerPrefix = "CN=IISIntegrationTest_Root";
1415

15-
public X509Certificate2 Certificate
16+
public X509Certificate2 GetOrCreateCertificate()
1617
{
17-
get
18+
if (_certificate != null)
1819
{
19-
if (_certificate != null)
20-
{
21-
return _certificate;
22-
}
23-
24-
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
25-
{
26-
store.Open(OpenFlags.ReadWrite);
27-
28-
foreach (var cert in store.Certificates)
29-
{
30-
if (cert.Issuer != "CN=IISIntegrationTest_Root")
31-
{
32-
continue;
33-
}
34-
_certificate = cert;
35-
store.Close();
36-
return cert;
37-
}
20+
return _certificate;
21+
}
3822

39-
var parentKey = CreateKeyMaterial(2048);
23+
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
24+
{
25+
store.Open(OpenFlags.ReadWrite);
26+
var parentKey = CreateKeyMaterial(2048);
4027

41-
// On first run of the test, creates the certificate in the trusted root certificate authorities.
42-
var parentRequest = new CertificateRequest("CN=IISIntegrationTest_Root", parentKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
28+
// Create a cert name with a random guid to avoid name conflicts
29+
var parentRequest = new CertificateRequest(
30+
_certIssuerPrefix + Guid.NewGuid().ToString(),
31+
parentKey, HashAlgorithmName.SHA256,
32+
RSASignaturePadding.Pkcs1);
4333

44-
parentRequest.CertificateExtensions.Add(
45-
new X509BasicConstraintsExtension(
46-
certificateAuthority: true,
47-
hasPathLengthConstraint: false,
48-
pathLengthConstraint: 0,
49-
critical: true));
34+
parentRequest.CertificateExtensions.Add(
35+
new X509BasicConstraintsExtension(
36+
certificateAuthority: true,
37+
hasPathLengthConstraint: false,
38+
pathLengthConstraint: 0,
39+
critical: true));
5040

51-
parentRequest.CertificateExtensions.Add(
52-
new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, critical: true));
41+
parentRequest.CertificateExtensions.Add(
42+
new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, critical: true));
5343

54-
parentRequest.CertificateExtensions.Add(
55-
new X509SubjectKeyIdentifierExtension(parentRequest.PublicKey, false));
44+
parentRequest.CertificateExtensions.Add(
45+
new X509SubjectKeyIdentifierExtension(parentRequest.PublicKey, false));
5646

57-
var notBefore = DateTimeOffset.Now.AddDays(-1);
58-
var notAfter = DateTimeOffset.Now.AddYears(5);
47+
var notBefore = DateTimeOffset.Now.AddDays(-1);
48+
var notAfter = DateTimeOffset.Now.AddYears(5);
5949

60-
var parentCert = parentRequest.CreateSelfSigned(notBefore, notAfter);
50+
var parentCert = parentRequest.CreateSelfSigned(notBefore, notAfter);
6151

62-
// Need to export/import the certificate to associate the private key with the cert.
63-
var imported = parentCert;
52+
// Need to export/import the certificate to associate the private key with the cert.
53+
var imported = parentCert;
6454

65-
var export = parentCert.Export(X509ContentType.Pkcs12, "");
66-
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
67-
Array.Clear(export, 0, export.Length);
55+
var export = parentCert.Export(X509ContentType.Pkcs12, "");
56+
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
57+
Array.Clear(export, 0, export.Length);
6858

69-
// Add the cert to the cert store
70-
_certificate = imported;
59+
// Add the cert to the cert store
60+
_certificate = imported;
7161

72-
store.Add(certificate: imported);
73-
store.Close();
74-
return imported;
75-
}
62+
store.Add(certificate: imported);
63+
store.Close();
64+
return imported;
7665
}
7766
}
7867

@@ -86,7 +75,17 @@ public void Dispose()
8675
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
8776
{
8877
store.Open(OpenFlags.ReadWrite);
89-
store.Remove(Certificate);
78+
store.Remove(_certificate);
79+
80+
// Remove any extra certs that were left by previous tests.
81+
for (var i = store.Certificates.Count - 1; i >= 0; i--)
82+
{
83+
var cert = store.Certificates[i];
84+
if (cert.Issuer.StartsWith(_certIssuerPrefix))
85+
{
86+
store.Remove(cert);
87+
}
88+
}
9089
store.Close();
9190
}
9291
}

test/Common.FunctionalTests/ClientCertificateTests.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Net.Http;
6+
using System.Security.Cryptography.X509Certificates;
57
using System.Threading.Tasks;
68
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
79
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
810
using Microsoft.AspNetCore.Server.IntegrationTesting;
911
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
1012
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
1113
using Microsoft.AspNetCore.Testing.xunit;
14+
using Microsoft.Extensions.Logging;
1215
using Xunit;
1316

1417
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
@@ -54,31 +57,51 @@ private async Task ClientCertTest(TestVariant variant, bool sendClientCert)
5457
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
5558
deploymentParameters.AddHttpsToServerConfig();
5659

57-
var deploymentResult = await DeployAsync(deploymentParameters);
5860
var handler = new HttpClientHandler
5961
{
6062
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
6163
ClientCertificateOptions = ClientCertificateOption.Manual,
6264
};
6365

66+
X509Certificate2 cert = null;
6467
if (sendClientCert)
6568
{
66-
Assert.NotNull(_certFixture.Certificate);
67-
handler.ClientCertificates.Add(_certFixture.Certificate);
69+
cert = _certFixture.GetOrCreateCertificate();
70+
handler.ClientCertificates.Add(cert);
6871
}
6972

73+
var deploymentResult = await DeployAsync(deploymentParameters);
74+
7075
var client = deploymentResult.CreateClient(handler);
7176
var response = await client.GetAsync("GetClientCert");
7277

7378
var responseText = await response.Content.ReadAsStringAsync();
7479

75-
if (sendClientCert)
80+
try
7681
{
77-
Assert.Equal($"Enabled;{_certFixture.Certificate.GetCertHashString()}", responseText);
82+
if (sendClientCert)
83+
{
84+
Assert.Equal($"Enabled;{cert.GetCertHashString()}", responseText);
85+
}
86+
else
87+
{
88+
Assert.Equal("Disabled", responseText);
89+
}
7890
}
79-
else
91+
catch (Exception ex)
8092
{
81-
Assert.Equal("Disabled", responseText);
93+
Logger.LogError($"Certificate is invalid. Issuer name: {cert.Issuer}");
94+
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
95+
{
96+
Logger.LogError($"List of current certificates in root store:");
97+
store.Open(OpenFlags.ReadWrite);
98+
foreach (var otherCert in store.Certificates)
99+
{
100+
Logger.LogError(otherCert.Issuer);
101+
}
102+
store.Close();
103+
}
104+
throw ex;
82105
}
83106
}
84107
}

0 commit comments

Comments
 (0)