From 36876f3ebf497ddd84724a7cf8168e33863699eb Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:58:28 -0400 Subject: [PATCH 1/2] Add new tests --- ...izationServiceCollectionExtensionsTests.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs b/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs index 2a4ed5548909..cec8a2a39d0f 100644 --- a/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs +++ b/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs @@ -257,6 +257,41 @@ public async Task CallHttpWithSelfSignedCert_SelfSignedCertificateConfigured_Wit Assert.Equal("Hi", response); } + [Fact] + public async Task CallHttp_ReachingOutToServerTrustedThroughSystemCA() + { + var services = CreateServices((gs, environment, config) => { }, services => + { + services.Configure(options => + { + options.AdditionalCustomTrustCertificates = []; + }); + }); + + var httpClient = services.GetRequiredService().CreateClient(); + + var response = await httpClient.GetAsync("https://example.com"); + response.EnsureSuccessStatusCode(); + } + + [Fact] + public async Task CallHttpWithCustomTrustForSelfSigned_ReachingOutToServerTrustedThroughSystemCA() + { + var selfSignedCertificate = CreateSelfSignedCert("localhost"); + var services = CreateServices((gs, environment, config) => { }, services => + { + services.Configure(options => + { + options.AdditionalCustomTrustCertificates = [selfSignedCertificate]; + }); + }); + + var httpClient = services.GetRequiredService().CreateClient(); + + var response = await httpClient.GetAsync("https://example.com"); + response.EnsureSuccessStatusCode(); + } + private static async Task CreateServerAsync(int port, Action configure) { var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); From 80a6afdadec09f1474bf237d77d1f42b4dbca3b9 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:58:53 -0400 Subject: [PATCH 2/2] Include root CA's in custom trust store --- .../Platform/X509ChainCustomization/X509ChainOptions.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Core/Platform/X509ChainCustomization/X509ChainOptions.cs b/src/Core/Platform/X509ChainCustomization/X509ChainOptions.cs index 189a1087f5df..6cd06acf3c1f 100644 --- a/src/Core/Platform/X509ChainCustomization/X509ChainOptions.cs +++ b/src/Core/Platform/X509ChainCustomization/X509ChainOptions.cs @@ -53,6 +53,10 @@ public bool TryGetCustomRemoteCertificateValidationCallback( return false; } + // Do this outside of the callback so that we aren't opening the root store every request. + using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine, OpenFlags.ReadOnly); + var rootCertificates = store.Certificates; + // Ref: https://github.com/dotnet/runtime/issues/39835#issuecomment-663020581 callback = (certificate, chain, errors) => { @@ -62,6 +66,10 @@ public bool TryGetCustomRemoteCertificateValidationCallback( } chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + + // We want our additional certificates to be in addition to the machines root store. + chain.ChainPolicy.CustomTrustStore.AddRange(rootCertificates); + foreach (var additionalCertificate in AdditionalCustomTrustCertificates) { chain.ChainPolicy.CustomTrustStore.Add(additionalCertificate);