Skip to content

Commit f31188b

Browse files
authored
Fix: Inconsistent certificate chain handling between endpoint and default configuration (#60710)
* Add certificate chain handling for certificate specified in the default section * Add a unit test to verify default certificate chain is loaded from the certificate path
1 parent 6ffab87 commit f31188b

File tree

5 files changed

+50
-1
lines changed

5 files changed

+50
-1
lines changed

Diff for: src/Servers/Kestrel/Core/src/IHttpsConfigurationService.cs

+10
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,21 @@ void ApplyHttpsConfiguration(
9090
internal readonly struct CertificateAndConfig
9191
{
9292
public readonly X509Certificate2 Certificate;
93+
public readonly X509Certificate2Collection CertificateChain;
9394
public readonly CertificateConfig CertificateConfig;
9495

9596
public CertificateAndConfig(X509Certificate2 certificate, CertificateConfig certificateConfig)
97+
: this(
98+
certificate,
99+
certificateConfig,
100+
[])
101+
{
102+
}
103+
104+
public CertificateAndConfig(X509Certificate2 certificate, CertificateConfig certificateConfig, X509Certificate2Collection certificateChain)
96105
{
97106
Certificate = certificate;
98107
CertificateConfig = certificateConfig;
108+
CertificateChain = certificateChain;
99109
}
100110
}

Diff for: src/Servers/Kestrel/Core/src/KestrelConfigurationLoader.cs

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ internal KestrelConfigurationLoader(
7777
private CertificateConfig? DefaultCertificateConfig { get; set; }
7878
internal X509Certificate2? DefaultCertificate { get; set; }
7979

80+
internal X509Certificate2Collection? DefaultCertificateChain { get; set; }
81+
8082
/// <summary>
8183
/// Specifies a configuration Action to run when an endpoint with the given name is loaded from configuration.
8284
/// </summary>
@@ -345,12 +347,14 @@ internal void ProcessEndpointsToAdd()
345347

346348
DefaultCertificateConfig = null;
347349
DefaultCertificate = null;
350+
DefaultCertificateChain = null;
348351

349352
ConfigurationReader = new ConfigurationReader(Configuration);
350353

351354
if (_httpsConfigurationService.IsInitialized && _httpsConfigurationService.LoadDefaultCertificate(ConfigurationReader) is CertificateAndConfig certPair)
352355
{
353356
DefaultCertificate = certPair.Certificate;
357+
DefaultCertificateChain = certPair.CertificateChain;
354358
DefaultCertificateConfig = certPair.CertificateConfig;
355359
}
356360

Diff for: src/Servers/Kestrel/Core/src/KestrelServerOptions.cs

+4
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ internal void ApplyDefaultCertificate(HttpsConnectionAdapterOptions httpsOptions
303303
if (ConfigurationLoader?.DefaultCertificate is X509Certificate2 certificateFromLoader)
304304
{
305305
httpsOptions.ServerCertificate = certificateFromLoader;
306+
if (ConfigurationLoader?.DefaultCertificateChain is X509Certificate2Collection certificateChainFromLoader)
307+
{
308+
httpsOptions.ServerCertificateChain = certificateChainFromLoader;
309+
}
306310
return;
307311
}
308312

Diff for: src/Servers/Kestrel/Core/src/TlsConfigurationLoader.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,13 @@ public ListenOptions UseHttpsWithSni(
128128
{
129129
if (configurationReader.Certificates.TryGetValue("Default", out var defaultCertConfig))
130130
{
131-
var (defaultCert, _ /* cert chain */) = _certificateConfigLoader.LoadCertificate(defaultCertConfig, "Default");
131+
var (defaultCert, defaultCertChain) = _certificateConfigLoader.LoadCertificate(defaultCertConfig, "Default");
132132
if (defaultCert != null)
133133
{
134+
if (defaultCertChain != null)
135+
{
136+
return new CertificateAndConfig(defaultCert, defaultCertConfig, defaultCertChain);
137+
}
134138
return new CertificateAndConfig(defaultCert, defaultCertConfig);
135139
}
136140
}

Diff for: src/Servers/Kestrel/Kestrel/test/KestrelConfigurationLoaderTests.cs

+27
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,33 @@ public void ConfigureDefaultsAppliesToNewConfigureEndpoints()
196196
Assert.False(serverOptions.CodeBackedListenOptions[0].IsTls);
197197
}
198198

199+
[Fact]
200+
public void ConfigureDefaultCertificatePathLoadsChain()
201+
{
202+
var serverOptions = CreateServerOptions();
203+
var testCertPath = TestResources.GetCertPath("leaf.com.crt");
204+
var ran1 = false;
205+
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
206+
{
207+
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
208+
new KeyValuePair<string,string>("Certificates:Default:Path", testCertPath)
209+
}).Build();
210+
211+
serverOptions.Configure(config)
212+
.Endpoint("End1", opt =>
213+
{
214+
ran1 = true;
215+
Assert.True(opt.IsHttps);
216+
Assert.NotNull(opt.HttpsOptions.ServerCertificate);
217+
Assert.NotNull(opt.HttpsOptions.ServerCertificateChain);
218+
Assert.Equal(2, opt.HttpsOptions.ServerCertificateChain.Count);
219+
}).Load();
220+
221+
Assert.True(ran1);
222+
223+
Assert.True(serverOptions.ConfigurationBackedListenOptions[0].IsTls);
224+
}
225+
199226
[Fact]
200227
public void ConfigureEndpointDefaultCanEnableHttps()
201228
{

0 commit comments

Comments
 (0)