diff --git a/samples/AppSettings/Program.cs b/samples/AppSettings/Program.cs
index 5db5710..13fbeeb 100644
--- a/samples/AppSettings/Program.cs
+++ b/samples/AppSettings/Program.cs
@@ -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();
}
}
diff --git a/src/Microsoft.AspNetCore/CertificateLoader.cs b/src/Microsoft.AspNetCore/CertificateLoader.cs
index be006b4..59115b0 100644
--- a/src/Microsoft.AspNetCore/CertificateLoader.cs
+++ b/src/Microsoft.AspNetCore/CertificateLoader.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore
{
@@ -15,14 +16,17 @@ 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;
///
- /// Creates a new instance of .
+ /// Creates a new instance of that can load certificate references from configuration.
///
- public CertificateLoader()
- : this(null)
+ /// An with information about certificates.
+ public CertificateLoader(IConfiguration certificatesConfiguration)
+ : this(certificatesConfiguration, null, null)
{
}
@@ -30,17 +34,35 @@ public CertificateLoader()
/// Creates a new instance of that can load certificate references from configuration.
///
/// An with information about certificates.
- public CertificateLoader(IConfiguration certificatesConfiguration)
- : this(certificatesConfiguration, new CertificateFileLoader(), new CertificateStoreLoader())
+ /// An instance.
+ public CertificateLoader(IConfiguration certificatesConfiguration, ILoggerFactory loggerFactory)
+ : this(certificatesConfiguration, loggerFactory, null)
{
- _certificatesConfiguration = certificatesConfiguration;
}
- internal CertificateLoader(IConfiguration certificatesConfiguration, ICertificateFileLoader certificateFileLoader, ICertificateStoreLoader certificateStoreLoader)
+ ///
+ /// Creates a new instance of that can load certificate references from configuration.
+ ///
+ /// An with information about certificates.
+ /// An instance.
+ /// The name of the environment the application is running in.
+ 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");
}
///
@@ -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);
@@ -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);
@@ -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;
@@ -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; }
@@ -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;
}
}
}
diff --git a/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs b/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs
index d2ec6f8..a76e7f6 100644
--- a/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs
+++ b/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs
@@ -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
{
+ 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)
@@ -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())
{
@@ -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);
diff --git a/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs
index 7cf3787..1db8765 100644
--- a/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs
+++ b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs
@@ -6,8 +6,9 @@
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Configuration;
-using Xunit;
+using Microsoft.Extensions.Logging;
using Moq;
+using Xunit;
namespace Microsoft.AspNetCore.FunctionalTests
{
@@ -35,6 +36,8 @@ public void Loads_SingleCertificateName_File()
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
Mock.Of());
@@ -45,31 +48,28 @@ public void Loads_SingleCertificateName_File()
}
[Fact]
- public void Throws_SingleCertificateName_File_KeyNotFound()
+ public void Throws_SingleCertificateName_KeyNotFound()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
{
- ["Certificates:Certificate1:Source"] = "File",
- ["Certificates:Certificate1:Path"] = "Certificate1.pfx",
- ["Certificates:Certificate1:Password"] = "Password1",
- ["TestConfig:Certificate"] = "Certificate2"
+ ["TestConfig:Certificate"] = "Certificate1"
})
.Build();
- var certificate = new X509Certificate2();
-
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
Mock.Of(),
Mock.Of());
- var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
- Assert.Equal("No certificate named Certificate2 found in configuration", exception.Message);
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal("No certificate named 'Certificate1' found in configuration for the current environment.", exception.Message);
}
[Fact]
- public void Throws_SingleCertificateName_File_FileNotFound()
+ public void Throws_SingleCertificateName_File_FileLoadError()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -81,19 +81,20 @@ public void Throws_SingleCertificateName_File_FileNotFound()
})
.Build();
- var exception = new Exception();
-
var certificateFileLoader = new Mock();
certificateFileLoader
.Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
- .Callback(() => throw exception);
+ .Callback(() => throw new Exception(nameof(Throws_SingleCertificateName_File_FileLoadError)));
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
Mock.Of());
- Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal($"Unable to load certificate from file 'Certificate1.pfx'. Error details: '{nameof(Throws_SingleCertificateName_File_FileLoadError)}'.", exception.Message);
}
[Fact]
@@ -119,6 +120,8 @@ public void Loads_SingleCertificateName_Store()
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -127,30 +130,7 @@ public void Loads_SingleCertificateName_Store()
Assert.Same(certificate, loadedCertificates.ElementAt(0));
certificateStoreLoader.VerifyAll();
}
-
- [Fact]
- public void Throws_SingleCertificateName_Store_KeyNotFound()
- {
- var configuration = new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary
- {
- ["Certificates:Certificate1:Source"] = "Store",
- ["Certificates:Certificate1:Subject"] = "localhost",
- ["Certificates:Certificate1:StoreName"] = "My",
- ["Certificates:Certificate1:StoreLocation"] = "CurrentUser",
- ["TestConfig:Certificate"] = "Certificate2"
- })
- .Build();
-
- var certificateLoader = new CertificateLoader(
- configuration.GetSection("Certificates"),
- Mock.Of(),
- Mock.Of());
-
- var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
- Assert.Equal("No certificate named Certificate2 found in configuration", exception.Message);
- }
-
+
[Fact]
public void ReturnsNull_SingleCertificateName_Store_NotFoundInStore()
{
@@ -172,6 +152,8 @@ public void ReturnsNull_SingleCertificateName_Store_NotFoundInStore()
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -209,6 +191,8 @@ public void Loads_MultipleCertificateNames_File()
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
Mock.Of());
@@ -219,51 +203,10 @@ public void Loads_MultipleCertificateNames_File()
certificateFileLoader.VerifyAll();
}
- [Theory]
- [InlineData("Certificate1;NotFound")]
- [InlineData("Certificate1;Certificate2;NotFound")]
- [InlineData("NotFound;Certificate1")]
- [InlineData("NotFound;Certificate1;Certificate2")]
- [InlineData("Certificate1;NotFound;Certificate2")]
- public void Throws_MultipleCertificateNames_File_KeyNotFound(string certificateNames)
- {
- var configuration = new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary
- {
- ["Certificates:Certificate1:Source"] = "File",
- ["Certificates:Certificate1:Path"] = "Certificate1.pfx",
- ["Certificates:Certificate1:Password"] = "Password1",
- ["Certificates:Certificate2:Source"] = "File",
- ["Certificates:Certificate2:Path"] = "Certificate2.pfx",
- ["Certificates:Certificate2:Password"] = "Password2",
- ["TestConfig:Certificate"] = certificateNames
- })
- .Build();
-
- var certificate1 = new X509Certificate2();
- var certificate2 = new X509Certificate2();
-
- var certificateFileLoader = new Mock();
- certificateFileLoader
- .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
- .Returns(certificate1);
- certificateFileLoader
- .Setup(loader => loader.Load("Certificate2.pfx", "Password2", It.IsAny()))
- .Returns(certificate2);
-
- var certificateLoader = new CertificateLoader(
- configuration.GetSection("Certificates"),
- certificateFileLoader.Object,
- Mock.Of());
-
- var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
- Assert.Equal("No certificate named NotFound found in configuration", exception.Message);
- }
-
[Theory]
[InlineData("Certificate1;Certificate2")]
[InlineData("Certificate2;Certificate1")]
- public void Throws_MultipleCertificateNames_File_FileNotFound(string certificateNames)
+ public void Throws_MultipleCertificateNames_File_FileLoadError(string certificateNames)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -279,7 +222,6 @@ public void Throws_MultipleCertificateNames_File_FileNotFound(string certificate
.Build();
var certificate1 = new X509Certificate2();
- var exception = new Exception();
var certificateFileLoader = new Mock();
certificateFileLoader
@@ -287,14 +229,17 @@ public void Throws_MultipleCertificateNames_File_FileNotFound(string certificate
.Returns(certificate1);
certificateFileLoader
.Setup(loader => loader.Load("Certificate2.pfx", "Password2", It.IsAny()))
- .Throws(exception);
+ .Throws(new Exception(nameof(Throws_MultipleCertificateNames_File_FileLoadError)));
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
Mock.Of());
- Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal($"Unable to load certificate from file 'Certificate2.pfx'. Error details: '{nameof(Throws_MultipleCertificateNames_File_FileLoadError)}'.", exception.Message);
}
[Fact]
@@ -328,6 +273,8 @@ public void Loads_MultipleCertificateNames_Store()
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -338,49 +285,6 @@ public void Loads_MultipleCertificateNames_Store()
certificateStoreLoader.VerifyAll();
}
- [Theory]
- [InlineData("Certificate1;NotFound")]
- [InlineData("Certificate1;Certificate2;NotFound")]
- [InlineData("NotFound;Certificate1")]
- [InlineData("NotFound;Certificate1;Certificate2")]
- [InlineData("Certificate1;NotFound;Certificate2")]
- public void Throws_MultipleCertificateNames_Store_KeyNotFound(string certificateNames)
- {
- var configuration = new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary
- {
- ["Certificates:Certificate1:Source"] = "Store",
- ["Certificates:Certificate1:Subject"] = "localhost",
- ["Certificates:Certificate1:StoreName"] = "My",
- ["Certificates:Certificate1:StoreLocation"] = "CurrentUser",
- ["Certificates:Certificate2:Source"] = "Store",
- ["Certificates:Certificate2:Subject"] = "example.com",
- ["Certificates:Certificate2:StoreName"] = "Root",
- ["Certificates:Certificate2:StoreLocation"] = "LocalMachine",
- ["TestConfig:Certificate"] = certificateNames
- })
- .Build();
-
- var certificate1 = new X509Certificate2();
- var certificate2 = new X509Certificate2();
-
- var certificateStoreLoader = new Mock();
- certificateStoreLoader
- .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny()))
- .Returns(certificate1);
- certificateStoreLoader
- .Setup(loader => loader.Load("example.com", "Root", StoreLocation.LocalMachine, It.IsAny()))
- .Returns(certificate2);
-
- var certificateLoader = new CertificateLoader(
- configuration.GetSection("Certificates"),
- Mock.Of(),
- certificateStoreLoader.Object);
-
- var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
- Assert.Equal("No certificate named NotFound found in configuration", exception.Message);
- }
-
[Theory]
[InlineData("Certificate1;Certificate2", 1)]
[InlineData("Certificate2;Certificate1", 1)]
@@ -421,6 +325,8 @@ public void ReturnsNull_MultipleCertificateNames_Store_NotFoundInStore(string ce
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -460,6 +366,8 @@ public void Loads_MultipleCertificateNames_FileAndStore()
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
@@ -475,7 +383,7 @@ public void Loads_MultipleCertificateNames_FileAndStore()
[InlineData("Certificate1;Certificate2;NotFound")]
[InlineData("Certificate1;NotFound;Certificate2")]
[InlineData("NotFound;Certificate1;Certificate2")]
- public void Throws_MultipleCertificateNames_FileAndStore_KeyNotFound(string certificateNames)
+ public void Throws_MultipleCertificateNames_KeyNotFound(string certificateNames)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -506,17 +414,19 @@ public void Throws_MultipleCertificateNames_FileAndStore_KeyNotFound(string cert
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
- var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
- Assert.Equal("No certificate named NotFound found in configuration", exception.Message);
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal("No certificate named 'NotFound' found in configuration for the current environment.", exception.Message);
}
[Theory]
[InlineData("Certificate1;Certificate2")]
[InlineData("Certificate2;Certificate1")]
- public void Throws_MultipleCertificateNames_FileAndStore_FileNotFound(string certificateNames)
+ public void Throws_MultipleCertificateNames_FileAndStore_FileLoadError(string certificateNames)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -532,13 +442,12 @@ public void Throws_MultipleCertificateNames_FileAndStore_FileNotFound(string cer
})
.Build();
- var exception = new Exception();
var storeCertificate = new X509Certificate2();
var certificateFileLoader = new Mock();
certificateFileLoader
.Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
- .Throws(exception);
+ .Throws(new Exception(nameof(Throws_MultipleCertificateNames_FileAndStore_FileLoadError)));
var certificateStoreLoader = new Mock();
certificateStoreLoader
@@ -547,10 +456,13 @@ public void Throws_MultipleCertificateNames_FileAndStore_FileNotFound(string cer
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
- Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal($"Unable to load certificate from file 'Certificate1.pfx'. Error details: '{nameof(Throws_MultipleCertificateNames_FileAndStore_FileLoadError)}'.", exception.Message);
}
[Theory]
@@ -586,6 +498,8 @@ public void ReturnsNull_MultipleCertificateNames_FileAndStore_NotFoundInStore(st
var certificateLoader = new CertificateLoader(
configuration.GetSection("Certificates"),
+ null,
+ null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
@@ -616,6 +530,8 @@ public void Loads_SingleCertificateInline_File()
.Returns(certificate);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
Mock.Of());
@@ -627,7 +543,7 @@ public void Loads_SingleCertificateInline_File()
}
[Fact]
- public void Throws_SingleCertificateInline_FileNotFound()
+ public void Throws_SingleCertificateInline_FileLoadError()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -638,19 +554,20 @@ public void Throws_SingleCertificateInline_FileNotFound()
})
.Build();
- var exception = new Exception();
-
var certificateFileLoader = new Mock();
certificateFileLoader
.Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
- .Throws(exception);
+ .Throws(new Exception(nameof(Throws_SingleCertificateInline_FileLoadError)));
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
Mock.Of());
- Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal($"Unable to load certificate from file 'Certificate1.pfx'. Error details: '{nameof(Throws_SingleCertificateInline_FileLoadError)}'.", exception.Message);
certificateFileLoader.VerifyAll();
}
@@ -675,6 +592,8 @@ public void Loads_SingleCertificateInline_Store()
.Returns(certificate);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -701,6 +620,8 @@ public void ReturnsNull_SingleCertificateInline_Store_NotFoundInStore()
var certificateStoreLoader = new Mock();
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -737,6 +658,8 @@ public void Loads_MultipleCertificatesInline_File()
.Returns(certificate2);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
Mock.Of());
@@ -749,7 +672,7 @@ public void Loads_MultipleCertificatesInline_File()
}
[Fact]
- public void Throws_MultipleCertificatesInline_File_FileNotFound()
+ public void Throws_MultipleCertificatesInline_File_FileLoadError()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -764,7 +687,6 @@ public void Throws_MultipleCertificatesInline_File_FileNotFound()
.Build();
var certificate1 = new X509Certificate2();
- var exception = new Exception();
var certificateFileLoader = new Mock();
certificateFileLoader
@@ -772,14 +694,17 @@ public void Throws_MultipleCertificatesInline_File_FileNotFound()
.Returns(certificate1);
certificateFileLoader
.Setup(loader => loader.Load("Certificate2.pfx", "Password2", It.IsAny()))
- .Throws(exception);
+ .Throws(new Exception(nameof(Throws_MultipleCertificatesInline_File_FileLoadError)));
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
Mock.Of());
- Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificates"))));
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")));
+ Assert.Equal($"Unable to load certificate from file 'Certificate2.pfx'. Error details: '{nameof(Throws_MultipleCertificatesInline_File_FileLoadError)}'.", exception.Message);
}
[Fact]
@@ -811,6 +736,8 @@ public void Loads_MultipleCertificatesInline_Store()
.Returns(certificate2);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -851,6 +778,8 @@ public void ReturnsNull_MultipleCertificatesInline_Store_NotFoundInStore()
.Returns(certificate);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
Mock.Of(),
certificateStoreLoader.Object);
@@ -908,6 +837,8 @@ public void Loads_MultipleCertificatesInline_FileAndStore()
.Returns(storeCertificate2);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
@@ -922,7 +853,7 @@ public void Loads_MultipleCertificatesInline_FileAndStore()
}
[Fact]
- public void Throws_MultipleCertificatesInline_FileAndStore_FileNotFound()
+ public void Throws_MultipleCertificatesInline_FileAndStore_FileLoadError()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary
@@ -937,13 +868,12 @@ public void Throws_MultipleCertificatesInline_FileAndStore_FileNotFound()
})
.Build();
- var exception = new Exception();
var certificate = new X509Certificate2();
var certificateFileLoader = new Mock();
certificateFileLoader
.Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
- .Throws(exception);
+ .Throws(new Exception(nameof(Throws_MultipleCertificatesInline_FileAndStore_FileLoadError)));
var certificateStoreLoader = new Mock();
certificateStoreLoader
@@ -951,11 +881,14 @@ public void Throws_MultipleCertificatesInline_FileAndStore_FileNotFound()
.Returns(certificate);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
- Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificates"))));
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")));
+ Assert.Equal($"Unable to load certificate from file 'Certificate1.pfx'. Error details: '{nameof(Throws_MultipleCertificatesInline_FileAndStore_FileLoadError)}'.", exception.Message);
}
[Fact]
@@ -987,6 +920,8 @@ public void ReturnsNull_MultipleCertificatesInline_FileAndStore_NotFoundInStore(
.Returns(null);
var certificateLoader = new CertificateLoader(
+ null,
+ null,
null,
certificateFileLoader.Object,
certificateStoreLoader.Object);
@@ -995,5 +930,115 @@ public void ReturnsNull_MultipleCertificatesInline_FileAndStore_NotFoundInStore(
Assert.Equal(1, loadedCertificates.Count());
Assert.Same(certificate, loadedCertificates.ElementAt(0));
}
+
+ [Theory]
+ [InlineData("Development")]
+ [InlineData("Production")]
+ public void IncludesEnvironmentNameInExceptionWhenAvailable(string environmentName)
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["TestConfig:Certificate"] = "Certificate1"
+ })
+ .Build();
+
+ var certificateLoader = new CertificateLoader(
+ configuration.GetSection("Certificates"),
+ null,
+ environmentName,
+ Mock.Of(),
+ Mock.Of());
+
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal($"No certificate named 'Certificate1' found in configuration for the current environment ({environmentName}).", exception.Message);
+ }
+
+ [Fact]
+ public void DoesNotIncludeEnvironmentNameInExceptionWhenNotAvailable()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["TestConfig:Certificate"] = "Certificate1"
+ })
+ .Build();
+
+ var certificateLoader = new CertificateLoader(
+ configuration.GetSection("Certificates"),
+ null,
+ null,
+ Mock.Of(),
+ Mock.Of());
+
+ var exception = Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")));
+ Assert.Equal("No certificate named 'Certificate1' found in configuration for the current environment.", exception.Message);
+ }
+
+ [Fact]
+ public void WarningLoggedWhenCertificateNotFoundInStore()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["TestConfig:Certificates:Certificate1:Source"] = "Store",
+ ["TestConfig:Certificates:Certificate1:Subject"] = "localhost",
+ ["TestConfig:Certificates:Certificate1:StoreName"] = "My",
+ ["TestConfig:Certificates:Certificate1:StoreLocation"] = "CurrentUser",
+ })
+ .Build();
+
+ var loggerFactory = new Mock();
+ var logger = new MockLogger();
+
+ loggerFactory
+ .Setup(factory => factory.CreateLogger("Microsoft.AspNetCore.CertificateLoader"))
+ .Returns(logger);
+
+ var certificateLoader = new CertificateLoader(
+ null,
+ loggerFactory.Object,
+ null,
+ Mock.Of(),
+ Mock.Of());
+
+ var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates"));
+ Assert.Equal(0, loadedCertificates.Count());
+ Assert.Single(logger.LogMessages, logMessage =>
+ logMessage.LogLevel == LogLevel.Warning &&
+ logMessage.Message == "Unable to find a matching certificate for subject 'localhost' in store 'My' in 'CurrentUser'.");
+ }
+
+ private class MockLogger : ILogger
+ {
+ private readonly List _logMessages = new List();
+
+ public IEnumerable LogMessages => _logMessages;
+
+ public IDisposable BeginScope(TState state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
+ {
+ _logMessages.Add(new LogMessage
+ {
+ LogLevel = logLevel,
+ Message = formatter(state, exception)
+ });
+ }
+
+ public class LogMessage
+ {
+ public LogLevel LogLevel { get; set; }
+ public string Message { get; set; }
+ }
+ }
}
}