Add mitigations to HttpsClientCert_GetCertInformation flakiness (#1529)
This commit is contained in:
parent
8f99140f30
commit
48d40e0e36
|
|
@ -14,8 +14,8 @@
|
||||||
"commandLineArgs": "$(IISExpressArguments)",
|
"commandLineArgs": "$(IISExpressArguments)",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
|
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
|
||||||
"ANCM_PATH": "$(AncmPath)",
|
"ANCM_PATH": "$(AspNetCoreModuleV1ShimDll)",
|
||||||
"ANCMV2_PATH": "$(AncmV2Path)",
|
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
|
||||||
"ANCM_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
|
"ANCM_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
|
||||||
"LAUNCHER_ARGS": "$(TargetPath)",
|
"LAUNCHER_ARGS": "$(TargetPath)",
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||||
|
|
@ -29,9 +29,9 @@
|
||||||
"commandLineArgs": "$(IISArguments)",
|
"commandLineArgs": "$(IISArguments)",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
|
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
|
||||||
"ANCM_PATH": "$(AncmPath)",
|
"ANCM_PATH": "$(AspNetCoreModuleV1ShimDll)",
|
||||||
"ANCMV2_PATH": "$(AncmV2Path)",
|
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
|
||||||
"ANCM_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
|
"ASPNETCORE_MODULE_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
|
||||||
"LAUNCHER_ARGS": "$(TargetPath)",
|
"LAUNCHER_ARGS": "$(TargetPath)",
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||||
"LAUNCHER_PATH": "$(DotNetPath)",
|
"LAUNCHER_PATH": "$(DotNetPath)",
|
||||||
|
|
|
||||||
|
|
@ -11,68 +11,57 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
public class ClientCertificateFixture : IDisposable
|
public class ClientCertificateFixture : IDisposable
|
||||||
{
|
{
|
||||||
private X509Certificate2 _certificate;
|
private X509Certificate2 _certificate;
|
||||||
|
private const string _certIssuerPrefix = "CN=IISIntegrationTest_Root";
|
||||||
|
|
||||||
public X509Certificate2 Certificate
|
public X509Certificate2 GetOrCreateCertificate()
|
||||||
{
|
{
|
||||||
get
|
if (_certificate != null)
|
||||||
{
|
{
|
||||||
if (_certificate != null)
|
return _certificate;
|
||||||
{
|
}
|
||||||
return _certificate;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
|
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
|
||||||
{
|
{
|
||||||
store.Open(OpenFlags.ReadWrite);
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
var parentKey = CreateKeyMaterial(2048);
|
||||||
|
|
||||||
foreach (var cert in store.Certificates)
|
// Create a cert name with a random guid to avoid name conflicts
|
||||||
{
|
var parentRequest = new CertificateRequest(
|
||||||
if (cert.Issuer != "CN=IISIntegrationTest_Root")
|
_certIssuerPrefix + Guid.NewGuid().ToString(),
|
||||||
{
|
parentKey, HashAlgorithmName.SHA256,
|
||||||
continue;
|
RSASignaturePadding.Pkcs1);
|
||||||
}
|
|
||||||
_certificate = cert;
|
|
||||||
store.Close();
|
|
||||||
return cert;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentKey = CreateKeyMaterial(2048);
|
parentRequest.CertificateExtensions.Add(
|
||||||
|
new X509BasicConstraintsExtension(
|
||||||
|
certificateAuthority: true,
|
||||||
|
hasPathLengthConstraint: false,
|
||||||
|
pathLengthConstraint: 0,
|
||||||
|
critical: true));
|
||||||
|
|
||||||
// On first run of the test, creates the certificate in the trusted root certificate authorities.
|
parentRequest.CertificateExtensions.Add(
|
||||||
var parentRequest = new CertificateRequest("CN=IISIntegrationTest_Root", parentKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, critical: true));
|
||||||
|
|
||||||
parentRequest.CertificateExtensions.Add(
|
parentRequest.CertificateExtensions.Add(
|
||||||
new X509BasicConstraintsExtension(
|
new X509SubjectKeyIdentifierExtension(parentRequest.PublicKey, false));
|
||||||
certificateAuthority: true,
|
|
||||||
hasPathLengthConstraint: false,
|
|
||||||
pathLengthConstraint: 0,
|
|
||||||
critical: true));
|
|
||||||
|
|
||||||
parentRequest.CertificateExtensions.Add(
|
var notBefore = DateTimeOffset.Now.AddDays(-1);
|
||||||
new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, critical: true));
|
var notAfter = DateTimeOffset.Now.AddYears(5);
|
||||||
|
|
||||||
parentRequest.CertificateExtensions.Add(
|
var parentCert = parentRequest.CreateSelfSigned(notBefore, notAfter);
|
||||||
new X509SubjectKeyIdentifierExtension(parentRequest.PublicKey, false));
|
|
||||||
|
|
||||||
var notBefore = DateTimeOffset.Now.AddDays(-1);
|
// Need to export/import the certificate to associate the private key with the cert.
|
||||||
var notAfter = DateTimeOffset.Now.AddYears(5);
|
var imported = parentCert;
|
||||||
|
|
||||||
var parentCert = parentRequest.CreateSelfSigned(notBefore, notAfter);
|
var export = parentCert.Export(X509ContentType.Pkcs12, "");
|
||||||
|
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
|
||||||
|
Array.Clear(export, 0, export.Length);
|
||||||
|
|
||||||
// Need to export/import the certificate to associate the private key with the cert.
|
// Add the cert to the cert store
|
||||||
var imported = parentCert;
|
_certificate = imported;
|
||||||
|
|
||||||
var export = parentCert.Export(X509ContentType.Pkcs12, "");
|
store.Add(certificate: imported);
|
||||||
imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
|
store.Close();
|
||||||
Array.Clear(export, 0, export.Length);
|
return imported;
|
||||||
|
|
||||||
// Add the cert to the cert store
|
|
||||||
_certificate = imported;
|
|
||||||
|
|
||||||
store.Add(certificate: imported);
|
|
||||||
store.Close();
|
|
||||||
return imported;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +75,17 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
|
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
|
||||||
{
|
{
|
||||||
store.Open(OpenFlags.ReadWrite);
|
store.Open(OpenFlags.ReadWrite);
|
||||||
store.Remove(Certificate);
|
store.Remove(_certificate);
|
||||||
|
|
||||||
|
// Remove any extra certs that were left by previous tests.
|
||||||
|
for (var i = store.Certificates.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var cert = store.Certificates[i];
|
||||||
|
if (cert.Issuer.StartsWith(_certIssuerPrefix))
|
||||||
|
{
|
||||||
|
store.Remove(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
store.Close();
|
store.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||||
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
|
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
|
||||||
|
|
@ -9,6 +11,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
|
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
|
|
@ -54,31 +57,51 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
|
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
|
||||||
deploymentParameters.AddHttpsToServerConfig();
|
deploymentParameters.AddHttpsToServerConfig();
|
||||||
|
|
||||||
var deploymentResult = await DeployAsync(deploymentParameters);
|
|
||||||
var handler = new HttpClientHandler
|
var handler = new HttpClientHandler
|
||||||
{
|
{
|
||||||
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
|
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
|
||||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
X509Certificate2 cert = null;
|
||||||
if (sendClientCert)
|
if (sendClientCert)
|
||||||
{
|
{
|
||||||
Assert.NotNull(_certFixture.Certificate);
|
cert = _certFixture.GetOrCreateCertificate();
|
||||||
handler.ClientCertificates.Add(_certFixture.Certificate);
|
handler.ClientCertificates.Add(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
var client = deploymentResult.CreateClient(handler);
|
var client = deploymentResult.CreateClient(handler);
|
||||||
var response = await client.GetAsync("GetClientCert");
|
var response = await client.GetAsync("GetClientCert");
|
||||||
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
if (sendClientCert)
|
try
|
||||||
{
|
{
|
||||||
Assert.Equal($"Enabled;{_certFixture.Certificate.GetCertHashString()}", responseText);
|
if (sendClientCert)
|
||||||
|
{
|
||||||
|
Assert.Equal($"Enabled;{cert.GetCertHashString()}", responseText);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Equal("Disabled", responseText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Assert.Equal("Disabled", responseText);
|
Logger.LogError($"Certificate is invalid. Issuer name: {cert.Issuer}");
|
||||||
|
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
|
||||||
|
{
|
||||||
|
Logger.LogError($"List of current certificates in root store:");
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
foreach (var otherCert in store.Certificates)
|
||||||
|
{
|
||||||
|
Logger.LogError(otherCert.Issuer);
|
||||||
|
}
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue