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

Show fwlink on HTTPS certificate errors (#83) #94

Merged
merged 11 commits into from
May 3, 2017
2 changes: 1 addition & 1 deletion samples/AppSettings/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static void Main(string[] args)
{
using (WebHost.Start(context => context.Response.WriteAsync("Hello, World!")))
{
Console.WriteLine("Running application: Press any key to shutdown...");
Console.WriteLine("Running application: Press any key to shutdown.");
Console.ReadKey();
}
}
Expand Down
60 changes: 46 additions & 14 deletions src/Microsoft.AspNetCore/CertificateLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Microsoft.AspNetCore
{
Expand All @@ -15,32 +16,53 @@ namespace Microsoft.AspNetCore
public class CertificateLoader
{
private readonly IConfiguration _certificatesConfiguration;
private readonly string _environmentName;
private readonly ICertificateFileLoader _certificateFileLoader;
private readonly ICertificateStoreLoader _certificateStoreLoader;
private readonly ILogger _logger;

/// <summary>
/// Creates a new instance of <see cref="CertificateLoader"/>.
/// Creates a new instance of <see cref="CertificateLoader"/> that can load certificate references from configuration.
/// </summary>
public CertificateLoader()
: this(null)
/// <param name="certificatesConfiguration">An <see cref="IConfiguration"/> with information about certificates.</param>
public CertificateLoader(IConfiguration certificatesConfiguration)
: this(certificatesConfiguration, null, null)
{
}

/// <summary>
/// Creates a new instance of <see cref="CertificateLoader"/> that can load certificate references from configuration.
/// </summary>
/// <param name="certificatesConfiguration">An <see cref="IConfiguration"/> with information about certificates.</param>
public CertificateLoader(IConfiguration certificatesConfiguration)
: this(certificatesConfiguration, new CertificateFileLoader(), new CertificateStoreLoader())
/// <param name="loggerFactory">An <see cref="ILoggerFactory"/> instance.</param>
public CertificateLoader(IConfiguration certificatesConfiguration, ILoggerFactory loggerFactory)
: this(certificatesConfiguration, loggerFactory, null)
{
_certificatesConfiguration = certificatesConfiguration;
}

internal CertificateLoader(IConfiguration certificatesConfiguration, ICertificateFileLoader certificateFileLoader, ICertificateStoreLoader certificateStoreLoader)
/// <summary>
/// Creates a new instance of <see cref="CertificateLoader"/> that can load certificate references from configuration.
/// </summary>
/// <param name="certificatesConfiguration">An <see cref="IConfiguration"/> with information about certificates.</param>
/// <param name="loggerFactory">An <see cref="ILoggerFactory"/> instance.</param>
/// <param name="environmentName">The name of the environment the application is running in.</param>
public CertificateLoader(IConfiguration certificatesConfiguration, ILoggerFactory loggerFactory, string environmentName)
: this(certificatesConfiguration, loggerFactory, environmentName, new CertificateFileLoader(), new CertificateStoreLoader())
{
}

internal CertificateLoader(
IConfiguration certificatesConfiguration,
ILoggerFactory loggerFactory,
string environmentName,
ICertificateFileLoader certificateFileLoader,
ICertificateStoreLoader certificateStoreLoader)
{
_environmentName = environmentName;
_certificatesConfiguration = certificatesConfiguration;
_certificateFileLoader = certificateFileLoader;
_certificateStoreLoader = certificateStoreLoader;
_logger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.CertificateLoader");
}

/// <summary>
Expand Down Expand Up @@ -99,7 +121,8 @@ private X509Certificate2 LoadSingle(string certificateName)

if (!certificateConfiguration.Exists())
{
throw new InvalidOperationException($"No certificate named {certificateName} found in configuration");
var environmentName = _environmentName != null ? $" ({_environmentName})" : "";
throw new KeyNotFoundException($"No certificate named '{certificateName}' found in configuration for the current environment{environmentName}.");
}

return LoadSingle(certificateConfiguration);
Expand All @@ -116,10 +139,10 @@ private X509Certificate2 LoadSingle(IConfigurationSection certificateConfigurati
certificateSource = new CertificateFileSource(_certificateFileLoader);
break;
case "store":
certificateSource = new CertificateStoreSource(_certificateStoreLoader);
certificateSource = new CertificateStoreSource(_certificateStoreLoader, _logger);
break;
default:
throw new InvalidOperationException($"Invalid certificate source kind: {sourceKind}");
throw new InvalidOperationException($"Invalid certificate source kind '{sourceKind}'.");
}

certificateConfiguration.Bind(certificateSource);
Expand Down Expand Up @@ -163,7 +186,7 @@ public override X509Certificate2 Load()

if (error != null)
{
throw error;
throw new InvalidOperationException($"Unable to load certificate from file '{Path}'. Error details: '{error.Message}'.", error);
}

return certificate;
Expand All @@ -188,10 +211,12 @@ private X509Certificate2 TryLoad(X509KeyStorageFlags flags, out Exception except
private class CertificateStoreSource : CertificateSource
{
private readonly ICertificateStoreLoader _certificateStoreLoader;
private readonly ILogger _logger;

public CertificateStoreSource(ICertificateStoreLoader certificateStoreLoader)
public CertificateStoreSource(ICertificateStoreLoader certificateStoreLoader, ILogger logger)
{
_certificateStoreLoader = certificateStoreLoader;
_logger = logger;
}

public string Subject { get; set; }
Expand All @@ -203,10 +228,17 @@ public override X509Certificate2 Load()
{
if (!Enum.TryParse(StoreLocation, ignoreCase: true, result: out StoreLocation storeLocation))
{
throw new InvalidOperationException($"Invalid store location: {StoreLocation}");
throw new InvalidOperationException($"The certificate store location '{StoreLocation}' is invalid.");
}

var certificate = _certificateStoreLoader.Load(Subject, StoreName, storeLocation, !AllowInvalid);

if (certificate == null)
{
_logger?.LogWarning($"Unable to find a matching certificate for subject '{Subject}' in store '{StoreName}' in '{StoreLocation}'.");
}

return _certificateStoreLoader.Load(Subject, StoreName, storeLocation, !AllowInvalid);
return certificate;
}
}
}
Expand Down
27 changes: 22 additions & 5 deletions src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@
using System;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore
{
internal class KestrelServerOptionsSetup : IConfigureOptions<KestrelServerOptions>
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IConfiguration _configurationRoot;
private readonly ILoggerFactory _loggerFactory;

public KestrelServerOptionsSetup(IConfiguration configurationRoot)
public KestrelServerOptionsSetup(
IHostingEnvironment hostingEnvironment,
IConfiguration configurationRoot,
ILoggerFactory loggerFactory)
{
_hostingEnvironment = hostingEnvironment;
_configurationRoot = configurationRoot;
_loggerFactory = loggerFactory;
}

public void Configure(KestrelServerOptions options)
Expand All @@ -27,7 +36,7 @@ public void Configure(KestrelServerOptions options)

private void BindConfiguration(KestrelServerOptions options)
{
var certificateLoader = new CertificateLoader(_configurationRoot.GetSection("Certificates"));
var certificateLoader = new CertificateLoader(_configurationRoot.GetSection("Certificates"), _loggerFactory, _hostingEnvironment.EnvironmentName);

foreach (var endPoint in _configurationRoot.GetSection("Kestrel:EndPoints").GetChildren())
{
Expand Down Expand Up @@ -56,14 +65,22 @@ private void BindEndPoint(
options.Listen(address, port, listenOptions =>
{
var certificateConfig = endPoint.GetSection("Certificate");
X509Certificate2 certificate;

if (certificateConfig.Exists())
{
var certificate = certificateLoader.Load(certificateConfig).FirstOrDefault();
try
{
certificate = certificateLoader.Load(certificateConfig).FirstOrDefault();

if (certificate == null)
if (certificate == null)
{
throw new InvalidOperationException($"No certificate found for endpoint '{endPoint.Key}'.");
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"Unable to load certificate for endpoint '{endPoint.Key}'");
throw new InvalidOperationException("Unable to configure HTTPS endpoint. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.", ex);
}

listenOptions.UseHttps(certificate);
Expand Down
Loading