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

Add SNI Support #2425

Merged
merged 1 commit into from
Apr 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15749</InternalAspNetCoreSdkPackageVersion>
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftExtensionsBuffersSourcesPackageVersion>
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsBuffersSourcesPackageVersion>
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview3-26331-01</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNetHttpHeadersPackageVersion>2.1.0-preview2-30478</MicrosoftNetHttpHeadersPackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNetHttpHeadersPackageVersion>2.1.0-preview2-30554</MicrosoftNetHttpHeadersPackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MoqPackageVersion>4.7.49</MoqPackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
<SystemBuffersPackageVersion>4.5.0-preview3-26331-02</SystemBuffersPackageVersion>
<SystemIOPipelinesPackageVersion>4.5.0-preview3-26331-02</SystemIOPipelinesPackageVersion>
<SystemMemoryPackageVersion>4.5.0-preview3-26331-02</SystemMemoryPackageVersion>
<SystemNumericsVectorsPackageVersion>4.5.0-preview3-26331-02</SystemNumericsVectorsPackageVersion>
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview3-26331-02</SystemRuntimeCompilerServicesUnsafePackageVersion>
<SystemSecurityCryptographyCngPackageVersion>4.5.0-preview3-26331-02</SystemSecurityCryptographyCngPackageVersion>
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview3-26331-02</SystemThreadingTasksExtensionsPackageVersion>
<SystemBuffersPackageVersion>4.5.0-preview2-26403-05</SystemBuffersPackageVersion>
<SystemIOPipelinesPackageVersion>4.5.0-preview2-26403-05</SystemIOPipelinesPackageVersion>
<SystemMemoryPackageVersion>4.5.0-preview2-26403-05</SystemMemoryPackageVersion>
<SystemNumericsVectorsPackageVersion>4.5.0-preview2-26403-05</SystemNumericsVectorsPackageVersion>
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview2-26403-05</SystemRuntimeCompilerServicesUnsafePackageVersion>
<SystemSecurityCryptographyCngPackageVersion>4.5.0-preview2-26403-05</SystemSecurityCryptographyCngPackageVersion>
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview2-26403-05</SystemThreadingTasksExtensionsPackageVersion>
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
Expand Down
18 changes: 16 additions & 2 deletions samples/SampleApp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -105,10 +106,23 @@ public static Task Main(string[] args)
listenOptions.UseHttps(StoreName.My, "localhost", allowInvalid: true);
});

options.ListenAnyIP(basePort + 5, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
var localhostCert = CertificateLoader.LoadFromStoreCert("localhost", "My", StoreLocation.CurrentUser, allowInvalid: true);
httpsOptions.ServerCertificateSelector = (features, name) =>
Copy link
Member

@halter73 halter73 Apr 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I like this just being a settable property. What about adding a RegisterCertificateSelectorCallback() method that takes a state object as well?

Copy link
Member Author

@Tratcher Tratcher Apr 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offline note: this is per endpoint, not per connection. No state is required to reduce allocations.

{
// Here you would check the name, select an appropriate cert, and provide a fallback or fail for null names.
return localhostCert;
};
});
});

options
.Configure()
.Endpoint(IPAddress.Loopback, basePort + 5)
.LocalhostEndpoint(basePort + 6)
.Endpoint(IPAddress.Loopback, basePort + 6)
.LocalhostEndpoint(basePort + 7)
.Load();

options
Expand Down
2 changes: 1 addition & 1 deletion src/Kestrel.Core/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@
<data name="PositiveTimeSpanRequired1" xml:space="preserve">
<value>Value must be a positive TimeSpan.</value>
</data>
<data name="ServiceCertificateRequired" xml:space="preserve">
<data name="ServerCertificateRequired" xml:space="preserve">
<value>The server certificate parameter is required.</value>
</data>
<data name="BindingToDefaultAddresses" xml:space="preserve">
Expand Down
14 changes: 13 additions & 1 deletion src/Kestrel.Core/HttpsConnectionAdapterOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;

namespace Microsoft.AspNetCore.Server.Kestrel.Https
Expand All @@ -29,14 +30,25 @@ public HttpsConnectionAdapterOptions()

/// <summary>
/// <para>
/// Specifies the server certificate used to authenticate HTTPS connections.
/// Specifies the server certificate used to authenticate HTTPS connections. This is ignored if ServerCertificateSelector is set.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we didn't have to set an order of precedence. Are there any alternatives?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several layers that can assign default certs, so the best option for now is to ignore it if a selector is provided. See #2422

/// </para>
/// <para>
/// 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).
/// </para>
/// </summary>
public X509Certificate2 ServerCertificate { get; set; }

/// <summary>
/// <para>
/// A callback that will be invoked to dynamically select a server certificate. This is higher priority than ServerCertificate.
/// If SNI is not avialable then the name parameter will be null.
/// </para>
/// <para>
/// 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).
/// </para>
/// </summary>
public Func<IFeatureCollection, string, X509Certificate2> ServerCertificateSelector { get; set; }

/// <summary>
/// Specifies the client certificate requirements for a HTTPS connection. Defaults to <see cref="ClientCertificateMode.NoCertificate"/>.
/// </summary>
Expand Down
52 changes: 45 additions & 7 deletions src/Kestrel.Core/Internal/HttpsConnectionAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class HttpsConnectionAdapter : IConnectionAdapter

private readonly HttpsConnectionAdapterOptions _options;
private readonly X509Certificate2 _serverCertificate;
private readonly Func<IFeatureCollection, string, X509Certificate2> _serverCertificateSelector;

private readonly ILogger _logger;

public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
Expand All @@ -36,15 +38,24 @@ public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options, ILoggerFact
throw new ArgumentNullException(nameof(options));
}

if (options.ServerCertificate == null)
// capture the certificate now so it can't be switched after validation
_serverCertificate = options.ServerCertificate;
_serverCertificateSelector = options.ServerCertificateSelector;
if (_serverCertificate == null && _serverCertificateSelector == null)
{
throw new ArgumentException(CoreStrings.ServiceCertificateRequired, nameof(options));
throw new ArgumentException(CoreStrings.ServerCertificateRequired, nameof(options));
}

// capture the certificate now so it can be switched after validation
_serverCertificate = options.ServerCertificate;

EnsureCertificateIsAllowedForServerAuth(_serverCertificate);
// If a selector is provided then ignore the cert, it may be a default cert.
if (_serverCertificateSelector != null)
{
// SslStream doesn't allow both.
_serverCertificate = null;
}
else
{
EnsureCertificateIsAllowedForServerAuth(_serverCertificate);
}

_options = options;
_logger = loggerFactory?.CreateLogger(nameof(HttpsConnectionAdapter));
Expand Down Expand Up @@ -115,9 +126,26 @@ private async Task<IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterC
try
{
#if NETCOREAPP2_1
// Adapt to the SslStream signature
ServerCertificateSelectionCallback selector = null;
if (_serverCertificateSelector != null)
{
selector = (sender, name) =>
{
context.Features.Set(sslStream);
var cert = _serverCertificateSelector(context.Features, name);
if (cert != null)
{
EnsureCertificateIsAllowedForServerAuth(cert);
}
return cert;
};
}

var sslOptions = new SslServerAuthenticationOptions()
{
ServerCertificate = _serverCertificate,
ServerCertificateSelectionCallback = selector,
ClientCertificateRequired = certificateRequired,
EnabledSslProtocols = _options.SslProtocols,
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
Expand All @@ -137,7 +165,17 @@ private async Task<IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterC

await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
#else
await sslStream.AuthenticateAsServerAsync(_serverCertificate, certificateRequired,
var serverCert = _serverCertificate;
if (_serverCertificateSelector != null)
{
context.Features.Set(sslStream);
serverCert = _serverCertificateSelector(context.Features, null);
if (serverCert != null)
{
EnsureCertificateIsAllowedForServerAuth(serverCert);
}
}
await sslStream.AuthenticateAsServerAsync(serverCert, certificateRequired,
_options.SslProtocols, _options.CheckCertificateRevocation);
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion src/Kestrel.Core/KestrelConfigurationLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public void Load()
// EndpointDefaults or configureEndpoint may have added an https adapter.
if (https && !listenOptions.ConnectionAdapters.Any(f => f.IsHttps))
{
if (httpsOptions.ServerCertificate == null)
if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
{
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Kestrel.Core/ListenOptionsHttpsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<Ht
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
configureOptions(options);

if (options.ServerCertificate == null)
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
{
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
}
Expand All @@ -192,7 +192,7 @@ internal static bool TryUseHttps(this ListenOptions listenOptions)
var options = new HttpsConnectionAdapterOptions();
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);

if (options.ServerCertificate == null)
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
{
return false;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Kestrel.Core/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading