Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 2ee0d6e

Browse files
committed
Delay loading the dev cert #2422
1 parent 953496a commit 2ee0d6e

File tree

6 files changed

+100
-45
lines changed

6 files changed

+100
-45
lines changed

src/Kestrel.Core/Internal/KestrelServerOptionsSetup.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Linq;
6-
using System.Security.Cryptography.X509Certificates;
7-
using Microsoft.AspNetCore.Certificates.Generation;
8-
using Microsoft.AspNetCore.Server.Kestrel.Internal;
9-
using Microsoft.Extensions.DependencyInjection;
10-
using Microsoft.Extensions.Logging;
115
using Microsoft.Extensions.Options;
126

137
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
@@ -24,33 +18,6 @@ public KestrelServerOptionsSetup(IServiceProvider services)
2418
public void Configure(KestrelServerOptions options)
2519
{
2620
options.ApplicationServices = _services;
27-
UseDefaultDeveloperCertificate(options);
28-
}
29-
30-
private void UseDefaultDeveloperCertificate(KestrelServerOptions options)
31-
{
32-
var logger = options.ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
33-
X509Certificate2 certificate = null;
34-
try
35-
{
36-
var certificateManager = new CertificateManager();
37-
certificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
38-
.FirstOrDefault();
39-
}
40-
catch
41-
{
42-
logger.UnableToLocateDevelopmentCertificate();
43-
}
44-
45-
if (certificate != null)
46-
{
47-
logger.LocatedDevelopmentCertificate(certificate);
48-
options.DefaultCertificate = certificate;
49-
}
50-
else
51-
{
52-
logger.UnableToLocateDevelopmentCertificate();
53-
}
5421
}
5522
}
5623
}

src/Kestrel.Core/KestrelConfigurationLoader.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ public void Load()
225225
// Specified
226226
httpsOptions.ServerCertificate = LoadCertificate(endpoint.Certificate, endpoint.Name)
227227
?? httpsOptions.ServerCertificate;
228+
229+
// Fallback
230+
Options.ApplyDefaultCert(httpsOptions);
228231
}
229232

230233
if (EndpointConfigurations.TryGetValue(endpoint.Name, out var configureEndpoint))

src/Kestrel.Core/KestrelServerOptions.cs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Net;
78
using System.Security.Cryptography.X509Certificates;
9+
using Microsoft.AspNetCore.Certificates.Generation;
810
using Microsoft.AspNetCore.Http;
911
using Microsoft.AspNetCore.Server.Kestrel.Https;
12+
using Microsoft.AspNetCore.Server.Kestrel.Internal;
1013
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
1114
using Microsoft.Extensions.Configuration;
15+
using Microsoft.Extensions.DependencyInjection;
16+
using Microsoft.Extensions.Logging;
1217

1318
namespace Microsoft.AspNetCore.Server.Kestrel.Core
1419
{
@@ -75,10 +80,15 @@ public class KestrelServerOptions
7580
private Action<HttpsConnectionAdapterOptions> HttpsDefaults { get; set; } = _ => { };
7681

7782
/// <summary>
78-
/// The default server certificate for https endpoints. This is applied before HttpsDefaults.
83+
/// The default server certificate for https endpoints. This is applied lazily after HttpsDefaults and user options.
7984
/// </summary>
8085
internal X509Certificate2 DefaultCertificate { get; set; }
8186

87+
/// <summary>
88+
/// Has the default dev certificate load been attempted?
89+
/// </summary>
90+
internal bool IsDevCertLoaded { get; set; }
91+
8292
/// <summary>
8393
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
8494
/// the prior action.
@@ -105,10 +115,49 @@ public void ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions> configu
105115

106116
internal void ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
107117
{
108-
httpsOptions.ServerCertificate = DefaultCertificate;
109118
HttpsDefaults(httpsOptions);
110119
}
111120

121+
internal void ApplyDefaultCert(HttpsConnectionAdapterOptions httpsOptions)
122+
{
123+
if (httpsOptions.ServerCertificate != null || httpsOptions.ServerCertificateSelector != null)
124+
{
125+
return;
126+
}
127+
128+
EnsureDefaultCert();
129+
130+
httpsOptions.ServerCertificate = DefaultCertificate;
131+
}
132+
133+
private void EnsureDefaultCert()
134+
{
135+
if (DefaultCertificate == null && !IsDevCertLoaded)
136+
{
137+
IsDevCertLoaded = true; // Only try once
138+
var logger = ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
139+
try
140+
{
141+
var certificateManager = new CertificateManager();
142+
DefaultCertificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
143+
.FirstOrDefault();
144+
145+
if (DefaultCertificate != null)
146+
{
147+
logger.LocatedDevelopmentCertificate(DefaultCertificate);
148+
}
149+
else
150+
{
151+
logger.UnableToLocateDevelopmentCertificate();
152+
}
153+
}
154+
catch
155+
{
156+
logger.UnableToLocateDevelopmentCertificate();
157+
}
158+
}
159+
}
160+
112161
/// <summary>
113162
/// Creates a configuration loader for setting up Kestrel.
114163
/// </summary>

src/Kestrel.Core/ListenOptionsHttpsExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<Ht
178178
var options = new HttpsConnectionAdapterOptions();
179179
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
180180
configureOptions(options);
181+
listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
181182

182183
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
183184
{
@@ -191,6 +192,7 @@ internal static bool TryUseHttps(this ListenOptions listenOptions)
191192
{
192193
var options = new HttpsConnectionAdapterOptions();
193194
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
195+
listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
194196

195197
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
196198
{

test/Kestrel.Core.Tests/KestrelServerTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ public void StartWithHttpsAddressConfiguresHttpsEndpoints()
6565
[Fact]
6666
public void KestrelServerThrowsUsefulExceptionIfDefaultHttpsProviderNotAdded()
6767
{
68-
using (var server = CreateServer(CreateServerOptions(), throwOnCriticalErrors: false))
68+
var options = CreateServerOptions();
69+
options.IsDevCertLoaded = true; // Prevent the system default from being loaded
70+
using (var server = CreateServer(options, throwOnCriticalErrors: false))
6971
{
7072
server.Features.Get<IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0");
7173

test/Kestrel.FunctionalTests/HttpsTests.cs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,38 +47,70 @@ public void UseHttpsDefaultsToDefaultCert()
4747
options.UseHttps();
4848
});
4949

50+
Assert.False(serverOptions.IsDevCertLoaded);
51+
5052
serverOptions.ListenLocalhost(5001, options =>
5153
{
5254
options.UseHttps(opt =>
5355
{
54-
Assert.Equal(defaultCert, opt.ServerCertificate);
56+
// The default cert is applied after UseHttps.
57+
Assert.Null(opt.ServerCertificate);
5558
});
5659
});
60+
Assert.False(serverOptions.IsDevCertLoaded);
5761
}
5862

5963
[Fact]
60-
public void ConfigureHttpsDefaultsOverridesDefaultCert()
64+
public void ConfigureHttpsDefaultsNeverLoadsDefaultCert()
6165
{
6266
var serverOptions = CreateServerOptions();
63-
var defaultCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
64-
serverOptions.DefaultCertificate = defaultCert;
67+
var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
6568
serverOptions.ConfigureHttpsDefaults(options =>
6669
{
67-
Assert.Equal(defaultCert, options.ServerCertificate);
68-
options.ServerCertificate = null;
70+
Assert.Null(options.ServerCertificate);
71+
options.ServerCertificate = testCert;
6972
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
7073
});
7174
serverOptions.ListenLocalhost(5000, options =>
7275
{
7376
options.UseHttps(opt =>
7477
{
75-
Assert.Null(opt.ServerCertificate);
78+
Assert.Equal(testCert, opt.ServerCertificate);
7679
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
80+
});
81+
});
82+
// Never lazy loaded
83+
Assert.False(serverOptions.IsDevCertLoaded);
84+
Assert.Null(serverOptions.DefaultCertificate);
85+
}
7786

78-
// So UseHttps won't throw
79-
opt.ServerCertificate = defaultCert;
87+
[Fact]
88+
public void ConfigureCertSelectorNeverLoadsDefaultCert()
89+
{
90+
var serverOptions = CreateServerOptions();
91+
var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
92+
serverOptions.ConfigureHttpsDefaults(options =>
93+
{
94+
Assert.Null(options.ServerCertificate);
95+
Assert.Null(options.ServerCertificateSelector);
96+
options.ServerCertificateSelector = (features, name) =>
97+
{
98+
return testCert;
99+
};
100+
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
101+
});
102+
serverOptions.ListenLocalhost(5000, options =>
103+
{
104+
options.UseHttps(opt =>
105+
{
106+
Assert.Null(opt.ServerCertificate);
107+
Assert.NotNull(opt.ServerCertificateSelector);
108+
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
80109
});
81110
});
111+
// Never lazy loaded
112+
Assert.False(serverOptions.IsDevCertLoaded);
113+
Assert.Null(serverOptions.DefaultCertificate);
82114
}
83115

84116
[Fact]

0 commit comments

Comments
 (0)