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

Commit f2818b8

Browse files
committed
Outline SNI APIs #2357
1 parent 54e538d commit f2818b8

7 files changed

+68
-16
lines changed

samples/SampleApp/Startup.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.AspNetCore.Hosting;
1313
using Microsoft.AspNetCore.Http;
1414
using Microsoft.AspNetCore.Server.Kestrel.Core;
15+
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
1516
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
1617
using Microsoft.Extensions.Configuration;
1718
using Microsoft.Extensions.Logging;
@@ -104,7 +105,24 @@ public static Task Main(string[] args)
104105
{
105106
listenOptions.UseHttps(StoreName.My, "localhost", allowInvalid: true);
106107
});
107-
108+
#if NETCOREAPP2_1
109+
options.ListenAnyIP(basePort + 5, listenOptions =>
110+
{
111+
listenOptions.UseHttps(httpsOptions =>
112+
{
113+
var localhostCert = CertificateLoader.LoadFromStoreCert("localhost", "My", StoreLocation.CurrentUser, allowInvalid: true);
114+
httpsOptions.ServerCertificateSelector = name =>
115+
{
116+
if (string.Equals(name, "localhost", StringComparison.OrdinalIgnoreCase))
117+
{
118+
return localhostCert;
119+
}
120+
// else fail?
121+
return null;
122+
};
123+
});
124+
});
125+
#endif
108126
options
109127
.Configure()
110128
.Endpoint(IPAddress.Loopback, basePort + 5)

src/Kestrel.Core/CoreStrings.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@
477477
<data name="PositiveTimeSpanRequired1" xml:space="preserve">
478478
<value>Value must be a positive TimeSpan.</value>
479479
</data>
480-
<data name="ServiceCertificateRequired" xml:space="preserve">
480+
<data name="ServerCertificateRequired" xml:space="preserve">
481481
<value>The server certificate parameter is required.</value>
482482
</data>
483483
<data name="BindingToDefaultAddresses" xml:space="preserve">

src/Kestrel.Core/HttpsConnectionAdapterOptions.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,26 @@ public HttpsConnectionAdapterOptions()
2929

3030
/// <summary>
3131
/// <para>
32-
/// Specifies the server certificate used to authenticate HTTPS connections.
32+
/// Specifies the server certificate used to authenticate HTTPS connections. This is ignored if ServerCertificateSelector is set.
3333
/// </para>
3434
/// <para>
3535
/// If the server certificate has an Extended Key Usage extension, the usages must include Server Authentication (OID 1.3.6.1.5.5.7.3.1).
3636
/// </para>
3737
/// </summary>
3838
public X509Certificate2 ServerCertificate { get; set; }
3939

40+
/// <summary>
41+
/// A callback that will be invoked to dynamically select a server certificate. This is higher priority than ServerCertificate.
42+
/// </summary>
43+
public Func<string, X509Certificate2> ServerCertificateSelector
44+
#if NETCOREAPP2_1
45+
{ get; set; }
46+
#else
47+
{
48+
get => null;
49+
set => throw new PlatformNotSupportedException("This API requires targeting netcoreapp2.1");
50+
}
51+
#endif
4052
/// <summary>
4153
/// Specifies the client certificate requirements for a HTTPS connection. Defaults to <see cref="ClientCertificateMode.NoCertificate"/>.
4254
/// </summary>

src/Kestrel.Core/Internal/HttpsConnectionAdapter.cs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class HttpsConnectionAdapter : IConnectionAdapter
2222

2323
private readonly HttpsConnectionAdapterOptions _options;
2424
private readonly X509Certificate2 _serverCertificate;
25+
private readonly Func<object, string, X509Certificate2> _serverCertificateSelector;
26+
2527
private readonly ILogger _logger;
2628

2729
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
@@ -36,15 +38,34 @@ public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options, ILoggerFact
3638
throw new ArgumentNullException(nameof(options));
3739
}
3840

39-
if (options.ServerCertificate == null)
41+
// capture the certificate now so it can't be switched after validation
42+
_serverCertificate = options.ServerCertificate;
43+
var selector = options.ServerCertificateSelector;
44+
if (_serverCertificate == null && selector == null)
4045
{
41-
throw new ArgumentException(CoreStrings.ServiceCertificateRequired, nameof(options));
46+
throw new ArgumentException(CoreStrings.ServerCertificateRequired, nameof(options));
4247
}
4348

44-
// capture the certificate now so it can be switched after validation
45-
_serverCertificate = options.ServerCertificate;
46-
47-
EnsureCertificateIsAllowedForServerAuth(_serverCertificate);
49+
// If a selector is provided then ignore the cert, it may be a default cert.
50+
if (selector != null)
51+
{
52+
// SslStream doesn't allow both.
53+
_serverCertificate = null;
54+
// Adapt to the SslStream signature
55+
_serverCertificateSelector = (sender, name) =>
56+
{
57+
var cert = selector(name);
58+
if (cert != null)
59+
{
60+
EnsureCertificateIsAllowedForServerAuth(cert);
61+
}
62+
return cert;
63+
};
64+
}
65+
else
66+
{
67+
EnsureCertificateIsAllowedForServerAuth(_serverCertificate);
68+
}
4869

4970
_options = options;
5071
_logger = loggerFactory?.CreateLogger(nameof(HttpsConnectionAdapter));
@@ -118,6 +139,7 @@ private async Task<IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterC
118139
var sslOptions = new SslServerAuthenticationOptions()
119140
{
120141
ServerCertificate = _serverCertificate,
142+
// TODO: ServerCertificateSelector = _serverCertificateSelector,
121143
ClientCertificateRequired = certificateRequired,
122144
EnabledSslProtocols = _options.SslProtocols,
123145
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,

src/Kestrel.Core/KestrelConfigurationLoader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public void Load()
236236
// EndpointDefaults or configureEndpoint may have added an https adapter.
237237
if (https && !listenOptions.ConnectionAdapters.Any(f => f.IsHttps))
238238
{
239-
if (httpsOptions.ServerCertificate == null)
239+
if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
240240
{
241241
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
242242
}

src/Kestrel.Core/ListenOptionsHttpsExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<Ht
179179
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
180180
configureOptions(options);
181181

182-
if (options.ServerCertificate == null)
182+
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
183183
{
184184
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
185185
}
@@ -192,7 +192,7 @@ internal static bool TryUseHttps(this ListenOptions listenOptions)
192192
var options = new HttpsConnectionAdapterOptions();
193193
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
194194

195-
if (options.ServerCertificate == null)
195+
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
196196
{
197197
return false;
198198
}

src/Kestrel.Core/Properties/CoreStrings.Designer.cs

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)