From 24f4fda3678e2d89167a893d14a86b10c2240027 Mon Sep 17 00:00:00 2001 From: Yang Pan <31965446+yangpanMS@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:23:39 -0800 Subject: [PATCH] Rotate certificate for unit testing (#404) --- .../Identity/CertificateManagerTest.cs | 44 +++++++++--------- .../AutoFixtureExtensions.cs | 4 +- .../Resources/test-certificate.private | Bin 0 -> 7348 bytes .../Resources/testcertificate2.private | Bin 7412 -> 0 bytes .../VirtualClient.TestExtensions.csproj | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 src/VirtualClient/VirtualClient.TestExtensions/Resources/test-certificate.private delete mode 100644 src/VirtualClient/VirtualClient.TestExtensions/Resources/testcertificate2.private diff --git a/src/VirtualClient/VirtualClient.Core.UnitTests/Identity/CertificateManagerTest.cs b/src/VirtualClient/VirtualClient.Core.UnitTests/Identity/CertificateManagerTest.cs index ea739bebad..6330f1150a 100644 --- a/src/VirtualClient/VirtualClient.Core.UnitTests/Identity/CertificateManagerTest.cs +++ b/src/VirtualClient/VirtualClient.Core.UnitTests/Identity/CertificateManagerTest.cs @@ -35,13 +35,13 @@ public void InitializeTest() [Test] [TestCase("AME")] [TestCase("GBL")] - [TestCase("AME Infra CA 02")] + [TestCase("AME Infra CA 06")] [TestCase("DC=AME")] [TestCase("DC=GBL")] [TestCase("CN=AME")] - [TestCase("CN=AME Infra CA 02")] - [TestCase("CN=AME Infra CA 02, DC=AME, DC=GBL")] - [TestCase("CN=AME Infra CA 02,DC=AME,DC=GBL")] + [TestCase("CN=AME Infra CA 06")] + [TestCase("CN=AME Infra CA 06, DC=AME, DC=GBL")] + [TestCase("CN=AME Infra CA 06,DC=AME,DC=GBL")] public void CertificateManagerSearchesSupportsARangeOfFormatsForIssuersOnCertificates(string issuer) { X509Certificate2 certificate = this.mockFixture.Create(); @@ -67,10 +67,10 @@ public void CertificateManagerDoesNotMismatchIssuersOnCertificates(string issuer [Test] [TestCase("virtualclient")] - [TestCase("virtualclient.corp")] - [TestCase("virtualclient.corp.azure.com")] - [TestCase("CN=virtualclient.corp")] - [TestCase("CN=virtualclient.corp.azure.com")] + [TestCase("virtualclient.test.corp")] + [TestCase("virtualclient.test.corp.azure.com")] + [TestCase("CN=virtualclient.test.corp")] + [TestCase("CN=virtualclient.test.corp.azure.com")] public void CertificateManagerSearchesSupportsARangeOfFormatsForSubjectNamesOnCertificates(string subjectName) { X509Certificate2 certificate = this.mockFixture.Create(); @@ -95,13 +95,13 @@ public async Task CertificateManagerSearchesTheExpectedDirectoryForCertificates( this.testCertificateManager = new TestCertificateManager(this.mockFixture); string expectedDirectory = CertificateManager.DefaultUnixCertificateDirectory; - string expectedCertificateFile = this.mockFixture.Combine(expectedDirectory, "A3706B2B12D35F8B2B5F8176F7B6F18534A23FAD"); + string expectedCertificateFile = this.mockFixture.Combine(expectedDirectory, "C3F4A77CAD588341B8D62EE4DA02D85E8F100EFA"); bool confirmedDir = false; bool confirmedFile = false; // Issuer: AME - // Subject Name: virtualclient.corp.azure.com - // Thumbprint: A3706B2B12D35F8B2B5F8176F7B6F18534A23FAD + // Subject Name: virtualclient.test.corp.azure.com + // Thumbprint: C3F4A77CAD588341B8D62EE4DA02D85E8F100EFA // // Note that this is an expired/invalid certificate so there are no security concerns. It is merely // used for testing purposes. @@ -138,35 +138,35 @@ public async Task CertificateManagerSearchesTheExpectedDirectoryForCertificates( // Expectation: // We do not need to compare the certificate properties. We just need to ensure we attempted to // read from the expected directory and that the certificate deserializes without error. - await this.testCertificateManager.GetCertificateFromPathAsync("AME", "virtualclient.corp.azure.com", expectedDirectory); + await this.testCertificateManager.GetCertificateFromPathAsync("AME", "virtualclient.test.corp.azure.com", expectedDirectory); Assert.IsTrue(confirmedDir); Assert.IsTrue(confirmedFile); } [Test] - [TestCase("AME", "virtualclient.corp.azure.com")] - [TestCase("GBL", "virtualclient.corp.azure.com")] - [TestCase("AME Infra CA 02", "virtualclient")] + [TestCase("AME", "virtualclient.test.corp.azure.com")] + [TestCase("GBL", "virtualclient.test.corp.azure.com")] + [TestCase("AME Infra CA 06", "virtualclient")] [TestCase("DC=AME", "corp.azure.com")] [TestCase("DC=GBL", "azure.com")] - [TestCase("CN=AME", "virtualclient.corp.azure.com")] - [TestCase("CN=AME Infra CA 02", "CN=virtualclient.corp.azure.com")] - [TestCase("CN=AME Infra CA 02, DC=AME, DC=GBL", "CN=virtualclient.corp.azure.com")] - [TestCase("CN=AME Infra CA 02,DC=AME,DC=GBL", "CN=virtualclient.corp.azure.com")] + [TestCase("CN=AME", "virtualclient.test.corp.azure.com")] + [TestCase("CN=AME Infra CA 06", "CN=virtualclient.test.corp.azure.com")] + [TestCase("CN=AME Infra CA 06, DC=AME, DC=GBL", "CN=virtualclient.test.corp.azure.com")] + [TestCase("CN=AME Infra CA 06,DC=AME,DC=GBL", "CN=virtualclient.test.corp.azure.com")] public async Task CertificateManagerHandlesDifferentIssuerAndSubjectNameFormats(string issuer, string subjectName) { this.mockFixture.Setup(PlatformID.Unix); this.testCertificateManager = new TestCertificateManager(this.mockFixture); string expectedDirectory = CertificateManager.DefaultUnixCertificateDirectory; - string expectedCertificateFile = this.mockFixture.Combine(expectedDirectory, "A3706B2B12D35F8B2B5F8176F7B6F18534A23FAD"); + string expectedCertificateFile = this.mockFixture.Combine(expectedDirectory, "C3F4A77CAD588341B8D62EE4DA02D85E8F100EFA"); bool confirmedDir = false; bool confirmedFile = false; // Issuer: AME - // Subject Name: virtualclient.corp.azure.com - // Thumbprint: A3706B2B12D35F8B2B5F8176F7B6F18534A23FAD + // Subject Name: virtualclient.test.corp.azure.com + // Thumbprint: C3F4A77CAD588341B8D62EE4DA02D85E8F100EFA // // Note that this is an expired/invalid certificate so there are no security concerns. It is merely // used for testing purposes. diff --git a/src/VirtualClient/VirtualClient.TestExtensions/AutoFixtureExtensions.cs b/src/VirtualClient/VirtualClient.TestExtensions/AutoFixtureExtensions.cs index 940616a8e2..d65b24f42a 100644 --- a/src/VirtualClient/VirtualClient.TestExtensions/AutoFixtureExtensions.cs +++ b/src/VirtualClient/VirtualClient.TestExtensions/AutoFixtureExtensions.cs @@ -58,14 +58,14 @@ private static X509Certificate2 CreateCertificate(bool withPrivateKey = false) if (withPrivateKey) { certificate = new X509Certificate2( - File.ReadAllBytes(Path.Combine(resourcesDirectory, "testcertificate2.private")), + File.ReadAllBytes(Path.Combine(resourcesDirectory, "test-certificate.private")), string.Empty.ToSecureString(), X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); } else { certificate = new X509Certificate2( - File.ReadAllBytes(Path.Combine(resourcesDirectory, "testcertificate2.private"))); + File.ReadAllBytes(Path.Combine(resourcesDirectory, "test-certificate.private"))); } return certificate; diff --git a/src/VirtualClient/VirtualClient.TestExtensions/Resources/test-certificate.private b/src/VirtualClient/VirtualClient.TestExtensions/Resources/test-certificate.private new file mode 100644 index 0000000000000000000000000000000000000000..71b630e7d176656c264dec4b2a77bd36e802f50c GIT binary patch literal 7348 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 YTefrtU-z|KGnI}eZ>~uH=fU{D0GF=DumAu6 literal 0 HcmV?d00001 diff --git a/src/VirtualClient/VirtualClient.TestExtensions/Resources/testcertificate2.private b/src/VirtualClient/VirtualClient.TestExtensions/Resources/testcertificate2.private deleted file mode 100644 index f7005e327cc42be4d93025696ddaa5b8eb0254ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7412 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 dHs=ockw`z&8760io`s(6Z*ozS{^!H_{{Su02Oa - + PreserveNewest