Add SNI support #2357
This commit is contained in:
parent
623c27ab01
commit
9ea2c50068
|
|
@ -7,39 +7,39 @@
|
||||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15749</InternalAspNetCoreSdkPackageVersion>
|
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15749</InternalAspNetCoreSdkPackageVersion>
|
||||||
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
|
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
|
||||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
|
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHostingPackageVersion>
|
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHostingPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
|
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreHttpPackageVersion>
|
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreHttpPackageVersion>
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreTestingPackageVersion>
|
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreTestingPackageVersion>
|
||||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview2-30554</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||||
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftExtensionsBuffersSourcesPackageVersion>
|
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsBuffersSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-a-preview2-mpr-16443</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingConsolePackageVersion>
|
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview2-30478</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview2-30554</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview3-26331-01</MicrosoftNETCoreApp21PackageVersion>
|
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<MicrosoftNetHttpHeadersPackageVersion>2.1.0-preview2-30478</MicrosoftNetHttpHeadersPackageVersion>
|
<MicrosoftNetHttpHeadersPackageVersion>2.1.0-preview2-30554</MicrosoftNetHttpHeadersPackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||||
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
||||||
<SystemBuffersPackageVersion>4.5.0-preview3-26331-02</SystemBuffersPackageVersion>
|
<SystemBuffersPackageVersion>4.5.0-preview2-26403-05</SystemBuffersPackageVersion>
|
||||||
<SystemIOPipelinesPackageVersion>4.5.0-preview3-26331-02</SystemIOPipelinesPackageVersion>
|
<SystemIOPipelinesPackageVersion>4.5.0-preview2-26403-05</SystemIOPipelinesPackageVersion>
|
||||||
<SystemMemoryPackageVersion>4.5.0-preview3-26331-02</SystemMemoryPackageVersion>
|
<SystemMemoryPackageVersion>4.5.0-preview2-26403-05</SystemMemoryPackageVersion>
|
||||||
<SystemNumericsVectorsPackageVersion>4.5.0-preview3-26331-02</SystemNumericsVectorsPackageVersion>
|
<SystemNumericsVectorsPackageVersion>4.5.0-preview2-26403-05</SystemNumericsVectorsPackageVersion>
|
||||||
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview3-26331-02</SystemRuntimeCompilerServicesUnsafePackageVersion>
|
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview2-26403-05</SystemRuntimeCompilerServicesUnsafePackageVersion>
|
||||||
<SystemSecurityCryptographyCngPackageVersion>4.5.0-preview3-26331-02</SystemSecurityCryptographyCngPackageVersion>
|
<SystemSecurityCryptographyCngPackageVersion>4.5.0-preview2-26403-05</SystemSecurityCryptographyCngPackageVersion>
|
||||||
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview3-26331-02</SystemThreadingTasksExtensionsPackageVersion>
|
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview2-26403-05</SystemThreadingTasksExtensionsPackageVersion>
|
||||||
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
||||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||||
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -105,10 +106,23 @@ namespace SampleApp
|
||||||
listenOptions.UseHttps(StoreName.My, "localhost", allowInvalid: true);
|
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) =>
|
||||||
|
{
|
||||||
|
// Here you would check the name, select an appropriate cert, and provide a fallback or fail for null names.
|
||||||
|
return localhostCert;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
options
|
options
|
||||||
.Configure()
|
.Configure()
|
||||||
.Endpoint(IPAddress.Loopback, basePort + 5)
|
.Endpoint(IPAddress.Loopback, basePort + 6)
|
||||||
.LocalhostEndpoint(basePort + 6)
|
.LocalhostEndpoint(basePort + 7)
|
||||||
.Load();
|
.Load();
|
||||||
|
|
||||||
options
|
options
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@
|
||||||
<data name="PositiveTimeSpanRequired1" xml:space="preserve">
|
<data name="PositiveTimeSpanRequired1" xml:space="preserve">
|
||||||
<value>Value must be a positive TimeSpan.</value>
|
<value>Value must be a positive TimeSpan.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceCertificateRequired" xml:space="preserve">
|
<data name="ServerCertificateRequired" xml:space="preserve">
|
||||||
<value>The server certificate parameter is required.</value>
|
<value>The server certificate parameter is required.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="BindingToDefaultAddresses" xml:space="preserve">
|
<data name="BindingToDefaultAddresses" xml:space="preserve">
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Net.Security;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
||||||
|
|
@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// <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.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <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).
|
/// 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).
|
||||||
|
|
@ -37,6 +38,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public X509Certificate2 ServerCertificate { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Specifies the client certificate requirements for a HTTPS connection. Defaults to <see cref="ClientCertificateMode.NoCertificate"/>.
|
/// Specifies the client certificate requirements for a HTTPS connection. Defaults to <see cref="ClientCertificateMode.NoCertificate"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
|
|
||||||
private readonly HttpsConnectionAdapterOptions _options;
|
private readonly HttpsConnectionAdapterOptions _options;
|
||||||
private readonly X509Certificate2 _serverCertificate;
|
private readonly X509Certificate2 _serverCertificate;
|
||||||
|
private readonly Func<IFeatureCollection, string, X509Certificate2> _serverCertificateSelector;
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
|
public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
|
||||||
|
|
@ -36,15 +38,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
throw new ArgumentNullException(nameof(options));
|
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
|
// If a selector is provided then ignore the cert, it may be a default cert.
|
||||||
_serverCertificate = options.ServerCertificate;
|
if (_serverCertificateSelector != null)
|
||||||
|
{
|
||||||
EnsureCertificateIsAllowedForServerAuth(_serverCertificate);
|
// SslStream doesn't allow both.
|
||||||
|
_serverCertificate = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnsureCertificateIsAllowedForServerAuth(_serverCertificate);
|
||||||
|
}
|
||||||
|
|
||||||
_options = options;
|
_options = options;
|
||||||
_logger = loggerFactory?.CreateLogger(nameof(HttpsConnectionAdapter));
|
_logger = loggerFactory?.CreateLogger(nameof(HttpsConnectionAdapter));
|
||||||
|
|
@ -115,9 +126,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#if NETCOREAPP2_1
|
#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()
|
var sslOptions = new SslServerAuthenticationOptions()
|
||||||
{
|
{
|
||||||
ServerCertificate = _serverCertificate,
|
ServerCertificate = _serverCertificate,
|
||||||
|
ServerCertificateSelectionCallback = selector,
|
||||||
ClientCertificateRequired = certificateRequired,
|
ClientCertificateRequired = certificateRequired,
|
||||||
EnabledSslProtocols = _options.SslProtocols,
|
EnabledSslProtocols = _options.SslProtocols,
|
||||||
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
|
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
|
||||||
|
|
@ -137,7 +165,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||||
|
|
||||||
await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
|
await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
|
||||||
#else
|
#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);
|
_options.SslProtocols, _options.CheckCertificateRevocation);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
// EndpointDefaults or configureEndpoint may have added an https adapter.
|
// EndpointDefaults or configureEndpoint may have added an https adapter.
|
||||||
if (https && !listenOptions.ConnectionAdapters.Any(f => f.IsHttps))
|
if (https && !listenOptions.ConnectionAdapters.Any(f => f.IsHttps))
|
||||||
{
|
{
|
||||||
if (httpsOptions.ServerCertificate == null)
|
if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
||||||
configureOptions(options);
|
configureOptions(options);
|
||||||
|
|
||||||
if (options.ServerCertificate == null)
|
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +192,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
var options = new HttpsConnectionAdapterOptions();
|
var options = new HttpsConnectionAdapterOptions();
|
||||||
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
||||||
|
|
||||||
if (options.ServerCertificate == null)
|
if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1693,16 +1693,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The server certificate parameter is required.
|
/// The server certificate parameter is required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ServiceCertificateRequired
|
internal static string ServerCertificateRequired
|
||||||
{
|
{
|
||||||
get => GetString("ServiceCertificateRequired");
|
get => GetString("ServerCertificateRequired");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The server certificate parameter is required.
|
/// The server certificate parameter is required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatServiceCertificateRequired()
|
internal static string FormatServerCertificateRequired()
|
||||||
=> GetString("ServiceCertificateRequired");
|
=> GetString("ServerCertificateRequired");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No listening endpoints were configured. Binding to {address0} and {address1} by default.
|
/// No listening endpoints were configured. Binding to {address0} and {address1} by default.
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
{
|
{
|
||||||
public class HttpsConnectionAdapterTests
|
public class HttpsConnectionAdapterTests
|
||||||
{
|
{
|
||||||
private static X509Certificate2 _x509Certificate2 = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
|
||||||
|
private static X509Certificate2 _x509Certificate2NoExt = TestResources.GetTestCertificate("no_extensions.pfx");
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
public HttpsConnectionAdapterTests(ITestOutputHelper output)
|
public HttpsConnectionAdapterTests(ITestOutputHelper output)
|
||||||
|
|
@ -148,6 +149,211 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UsesProvidedServerCertificateSelector()
|
||||||
|
{
|
||||||
|
var selectorCalled = 0;
|
||||||
|
var serviceContext = new TestServiceContext();
|
||||||
|
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||||
|
{
|
||||||
|
ConnectionAdapters =
|
||||||
|
{
|
||||||
|
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
||||||
|
{
|
||||||
|
ServerCertificateSelector = (features, name) =>
|
||||||
|
{
|
||||||
|
Assert.NotNull(features);
|
||||||
|
Assert.NotNull(features.Get<SslStream>());
|
||||||
|
#if NETCOREAPP2_1
|
||||||
|
Assert.Equal("localhost", name);
|
||||||
|
#else
|
||||||
|
Assert.Null(name);
|
||||||
|
#endif
|
||||||
|
selectorCalled++;
|
||||||
|
return _x509Certificate2;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using (var server = new TestServer(context => Task.CompletedTask, serviceContext, listenOptions))
|
||||||
|
{
|
||||||
|
using (var client = new TcpClient())
|
||||||
|
{
|
||||||
|
// SslStream is used to ensure the certificate is actually passed to the server
|
||||||
|
// HttpClient might not send the certificate because it is invalid or it doesn't match any
|
||||||
|
// of the certificate authorities sent by the server in the SSL handshake.
|
||||||
|
var stream = await OpenSslStream(client, server);
|
||||||
|
await stream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false);
|
||||||
|
Assert.True(stream.RemoteCertificate.Equals(_x509Certificate2));
|
||||||
|
Assert.Equal(1, selectorCalled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UsesProvidedServerCertificateSelectorEachTime()
|
||||||
|
{
|
||||||
|
var selectorCalled = 0;
|
||||||
|
var serviceContext = new TestServiceContext();
|
||||||
|
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||||
|
{
|
||||||
|
ConnectionAdapters =
|
||||||
|
{
|
||||||
|
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
||||||
|
{
|
||||||
|
ServerCertificateSelector = (features, name) =>
|
||||||
|
{
|
||||||
|
Assert.NotNull(features);
|
||||||
|
Assert.NotNull(features.Get<SslStream>());
|
||||||
|
#if NETCOREAPP2_1
|
||||||
|
Assert.Equal("localhost", name);
|
||||||
|
#else
|
||||||
|
Assert.Null(name);
|
||||||
|
#endif
|
||||||
|
selectorCalled++;
|
||||||
|
if (selectorCalled == 1)
|
||||||
|
{
|
||||||
|
return _x509Certificate2;
|
||||||
|
}
|
||||||
|
return _x509Certificate2NoExt;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using (var server = new TestServer(context => Task.CompletedTask, serviceContext, listenOptions))
|
||||||
|
{
|
||||||
|
using (var client = new TcpClient())
|
||||||
|
{
|
||||||
|
// SslStream is used to ensure the certificate is actually passed to the server
|
||||||
|
// HttpClient might not send the certificate because it is invalid or it doesn't match any
|
||||||
|
// of the certificate authorities sent by the server in the SSL handshake.
|
||||||
|
var stream = await OpenSslStream(client, server);
|
||||||
|
await stream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false);
|
||||||
|
Assert.True(stream.RemoteCertificate.Equals(_x509Certificate2));
|
||||||
|
Assert.Equal(1, selectorCalled);
|
||||||
|
}
|
||||||
|
using (var client = new TcpClient())
|
||||||
|
{
|
||||||
|
// SslStream is used to ensure the certificate is actually passed to the server
|
||||||
|
// HttpClient might not send the certificate because it is invalid or it doesn't match any
|
||||||
|
// of the certificate authorities sent by the server in the SSL handshake.
|
||||||
|
var stream = await OpenSslStream(client, server);
|
||||||
|
await stream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false);
|
||||||
|
Assert.True(stream.RemoteCertificate.Equals(_x509Certificate2NoExt));
|
||||||
|
Assert.Equal(2, selectorCalled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UsesProvidedServerCertificateSelectorValidatesEkus()
|
||||||
|
{
|
||||||
|
var selectorCalled = 0;
|
||||||
|
var serviceContext = new TestServiceContext();
|
||||||
|
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||||
|
{
|
||||||
|
ConnectionAdapters =
|
||||||
|
{
|
||||||
|
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
||||||
|
{
|
||||||
|
ServerCertificateSelector = (features, name) =>
|
||||||
|
{
|
||||||
|
selectorCalled++;
|
||||||
|
return TestResources.GetTestCertificate("eku.code_signing.pfx");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using (var server = new TestServer(context => Task.CompletedTask, serviceContext, listenOptions))
|
||||||
|
{
|
||||||
|
using (var client = new TcpClient())
|
||||||
|
{
|
||||||
|
// SslStream is used to ensure the certificate is actually passed to the server
|
||||||
|
// HttpClient might not send the certificate because it is invalid or it doesn't match any
|
||||||
|
// of the certificate authorities sent by the server in the SSL handshake.
|
||||||
|
var stream = await OpenSslStream(client, server);
|
||||||
|
await Assert.ThrowsAsync<IOException>(() =>
|
||||||
|
stream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false));
|
||||||
|
Assert.Equal(1, selectorCalled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UsesProvidedServerCertificateSelectorOverridesServerCertificate()
|
||||||
|
{
|
||||||
|
var selectorCalled = 0;
|
||||||
|
var serviceContext = new TestServiceContext();
|
||||||
|
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||||
|
{
|
||||||
|
ConnectionAdapters =
|
||||||
|
{
|
||||||
|
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
||||||
|
{
|
||||||
|
ServerCertificate = _x509Certificate2NoExt,
|
||||||
|
ServerCertificateSelector = (features, name) =>
|
||||||
|
{
|
||||||
|
Assert.NotNull(features);
|
||||||
|
Assert.NotNull(features.Get<SslStream>());
|
||||||
|
#if NETCOREAPP2_1
|
||||||
|
Assert.Equal("localhost", name);
|
||||||
|
#else
|
||||||
|
Assert.Null(name);
|
||||||
|
#endif
|
||||||
|
selectorCalled++;
|
||||||
|
return _x509Certificate2;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using (var server = new TestServer(context => Task.CompletedTask, serviceContext, listenOptions))
|
||||||
|
{
|
||||||
|
using (var client = new TcpClient())
|
||||||
|
{
|
||||||
|
// SslStream is used to ensure the certificate is actually passed to the server
|
||||||
|
// HttpClient might not send the certificate because it is invalid or it doesn't match any
|
||||||
|
// of the certificate authorities sent by the server in the SSL handshake.
|
||||||
|
var stream = await OpenSslStream(client, server);
|
||||||
|
await stream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false);
|
||||||
|
Assert.True(stream.RemoteCertificate.Equals(_x509Certificate2));
|
||||||
|
Assert.Equal(1, selectorCalled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UsesProvidedServerCertificateSelectorFailsIfYouReturnNull()
|
||||||
|
{
|
||||||
|
var selectorCalled = 0;
|
||||||
|
var serviceContext = new TestServiceContext();
|
||||||
|
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||||
|
{
|
||||||
|
ConnectionAdapters =
|
||||||
|
{
|
||||||
|
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions
|
||||||
|
{
|
||||||
|
ServerCertificateSelector = (features, name) =>
|
||||||
|
{
|
||||||
|
selectorCalled++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using (var server = new TestServer(context => Task.CompletedTask, serviceContext, listenOptions))
|
||||||
|
{
|
||||||
|
using (var client = new TcpClient())
|
||||||
|
{
|
||||||
|
// SslStream is used to ensure the certificate is actually passed to the server
|
||||||
|
// HttpClient might not send the certificate because it is invalid or it doesn't match any
|
||||||
|
// of the certificate authorities sent by the server in the SSL handshake.
|
||||||
|
var stream = await OpenSslStream(client, server);
|
||||||
|
await Assert.ThrowsAsync<IOException>(() =>
|
||||||
|
stream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false));
|
||||||
|
Assert.Equal(1, selectorCalled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CertificatePassedToHttpContext()
|
public async Task CertificatePassedToHttpContext()
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,10 @@ namespace Microsoft.AspNetCore.Testing
|
||||||
{
|
{
|
||||||
return new X509Certificate2(TestCertificatePath, "testPassword");
|
return new X509Certificate2(TestCertificatePath, "testPassword");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static X509Certificate2 GetTestCertificate(string certName)
|
||||||
|
{
|
||||||
|
return new X509Certificate2(GetCertPath(certName), "testPassword");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue