diff --git a/MetaPackages.sln b/MetaPackages.sln index ab793361cf..bac992e613 100644 --- a/MetaPackages.sln +++ b/MetaPackages.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26507.0 +VisualStudioVersion = 15.0.26524.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED834E68-51C3-4ADE-ACC8-6BA6D4207C09}" EndProject @@ -10,6 +10,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.All", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{97D53BEB-A511-4FBE-B784-AB407D9A219F}" ProjectSection(SolutionItems) = preProject + NuGet.config = NuGet.config version.props = version.props EndProjectSection EndProject diff --git a/build/common.props b/build/common.props index 4d4fc25989..2ae8536639 100644 --- a/build/common.props +++ b/build/common.props @@ -55,6 +55,7 @@ + diff --git a/src/Microsoft.AspNetCore/CertificateFileLoader.cs b/src/Microsoft.AspNetCore/CertificateFileLoader.cs deleted file mode 100644 index 1c0b6e5759..0000000000 --- a/src/Microsoft.AspNetCore/CertificateFileLoader.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Security.Cryptography.X509Certificates; - -namespace Microsoft.AspNetCore -{ - internal class CertificateFileLoader : ICertificateFileLoader - { - public X509Certificate2 Load(string path, string password, X509KeyStorageFlags flags) - { - return new X509Certificate2(path, password, flags); - } - } -} diff --git a/src/Microsoft.AspNetCore/CertificateLoader.cs b/src/Microsoft.AspNetCore/CertificateLoader.cs deleted file mode 100644 index b43bdb8fcc..0000000000 --- a/src/Microsoft.AspNetCore/CertificateLoader.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore -{ - /// - /// A helper class to load certificates from files and certificate stores based on data. - /// - 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 that can load certificate references from configuration. - /// - /// An with information about certificates. - public CertificateLoader(IConfiguration certificatesConfiguration) - : this(certificatesConfiguration, null, null) - { - } - - /// - /// Creates a new instance of that can load certificate references from configuration. - /// - /// An with information about certificates. - /// An instance. - public CertificateLoader(IConfiguration certificatesConfiguration, ILoggerFactory loggerFactory) - : this(certificatesConfiguration, loggerFactory, null) - { - } - - /// - /// 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"); - } - - /// - /// Loads one or more certificates based on the information found in a configuration section. - /// - /// A configuration section containing either a string value referencing certificates - /// by name, or one or more inline certificate specifications. - /// - /// One or more loaded certificates. - public IEnumerable Load(IConfigurationSection certificateConfiguration) - { - var certificateNames = certificateConfiguration.Value; - var certificates = new List(); - - if (certificateNames != null) - { - foreach (var certificateName in certificateNames.Split(';')) - { - var certificate = LoadSingle(certificateName); - if (certificate != null) - { - certificates.Add(certificate); - } - } - } - else - { - if (certificateConfiguration["Source"] != null) - { - var certificate = LoadSingle(certificateConfiguration); - if (certificate != null) - { - certificates.Add(certificate); - } - } - else - { - certificates.AddRange(LoadMultiple(certificateConfiguration)); - } - } - - return certificates; - } - - /// - /// Loads a certificate by name. - /// - /// The certificate name. - /// The loaded certificate - /// This method only works if the instance was constructed with - /// a reference to an instance containing named certificates. - /// - private X509Certificate2 LoadSingle(string certificateName) - { - var certificateConfiguration = _certificatesConfiguration?.GetSection(certificateName); - - if (!certificateConfiguration.Exists()) - { - var environmentName = _environmentName != null ? $" ({_environmentName})" : ""; - throw new KeyNotFoundException($"No certificate named '{certificateName}' found in configuration for the current environment{environmentName}."); - } - - return LoadSingle(certificateConfiguration); - } - - private X509Certificate2 LoadSingle(IConfigurationSection certificateConfiguration) - { - var sourceKind = certificateConfiguration["Source"]; - - CertificateSource certificateSource; - switch (sourceKind.ToLowerInvariant()) - { - case "file": - certificateSource = new CertificateFileSource(_certificateFileLoader); - break; - case "store": - certificateSource = new CertificateStoreSource(_certificateStoreLoader, _logger); - break; - default: - throw new InvalidOperationException($"Invalid certificate source kind '{sourceKind}'."); - } - - certificateConfiguration.Bind(certificateSource); - - return certificateSource.Load(); - } - - private IEnumerable LoadMultiple(IConfigurationSection certificatesConfiguration) - => certificatesConfiguration.GetChildren() - .Select(LoadSingle) - .Where(c => c != null); - - private abstract class CertificateSource - { - public string Source { get; set; } - - public abstract X509Certificate2 Load(); - } - - private class CertificateFileSource : CertificateSource - { - private ICertificateFileLoader _certificateFileLoader; - - public CertificateFileSource(ICertificateFileLoader certificateFileLoader) - { - _certificateFileLoader = certificateFileLoader; - } - - public string Path { get; set; } - - public string Password { get; set; } - - public override X509Certificate2 Load() - { - var certificate = TryLoad(X509KeyStorageFlags.DefaultKeySet, out var error) - ?? TryLoad(X509KeyStorageFlags.UserKeySet, out error) -#if NETCOREAPP2_0 - ?? TryLoad(X509KeyStorageFlags.EphemeralKeySet, out error) -#elif NET461 -#else -#error target frameworks need to be updated -#endif - ; - - if (error != null) - { - throw new InvalidOperationException($"Unable to load certificate from file '{Path}'. Error details: '{error.Message}'.", error); - } - - return certificate; - } - - private X509Certificate2 TryLoad(X509KeyStorageFlags flags, out Exception exception) - { - try - { - var loadedCertificate = _certificateFileLoader.Load(Path, Password, flags); - exception = null; - return loadedCertificate; - } - catch (Exception e) - { - exception = e; - return null; - } - } - } - - private class CertificateStoreSource : CertificateSource - { - private readonly ICertificateStoreLoader _certificateStoreLoader; - private readonly ILogger _logger; - - public CertificateStoreSource(ICertificateStoreLoader certificateStoreLoader, ILogger logger) - { - _certificateStoreLoader = certificateStoreLoader; - _logger = logger; - } - - public string Subject { get; set; } - public string StoreName { get; set; } - public string StoreLocation { get; set; } - public bool AllowInvalid { get; set; } - - public override X509Certificate2 Load() - { - if (!Enum.TryParse(StoreLocation, ignoreCase: true, result: out StoreLocation 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 certificate; - } - } - } -} diff --git a/src/Microsoft.AspNetCore/CertificateStoreLoader.cs b/src/Microsoft.AspNetCore/CertificateStoreLoader.cs deleted file mode 100644 index 27158d4da3..0000000000 --- a/src/Microsoft.AspNetCore/CertificateStoreLoader.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using System.Security.Cryptography.X509Certificates; - -namespace Microsoft.AspNetCore -{ - internal class CertificateStoreLoader : ICertificateStoreLoader - { - public X509Certificate2 Load(string subject, string storeName, StoreLocation storeLocation, bool validOnly) - { - using (var store = new X509Store(storeName, storeLocation)) - { - X509Certificate2Collection storeCertificates = null; - X509Certificate2Collection foundCertificates = null; - X509Certificate2 foundCertificate = null; - - try - { - store.Open(OpenFlags.ReadOnly); - storeCertificates = store.Certificates; - foundCertificates = storeCertificates.Find(X509FindType.FindBySubjectDistinguishedName, subject, validOnly); - foundCertificate = foundCertificates - .OfType() - .OrderByDescending(certificate => certificate.NotAfter) - .FirstOrDefault(); - - return foundCertificate; - } - finally - { - if (foundCertificate != null) - { - storeCertificates.Remove(foundCertificate); - foundCertificates.Remove(foundCertificate); - } - - DisposeCertificates(storeCertificates); - DisposeCertificates(foundCertificates); - } - } - } - - private void DisposeCertificates(X509Certificate2Collection certificates) - { - if (certificates != null) - { - foreach (var certificate in certificates) - { - certificate.Dispose(); - } - } - } - } -} diff --git a/src/Microsoft.AspNetCore/ICertificateFileLoader.cs b/src/Microsoft.AspNetCore/ICertificateFileLoader.cs deleted file mode 100644 index 4c394bbd02..0000000000 --- a/src/Microsoft.AspNetCore/ICertificateFileLoader.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Security.Cryptography.X509Certificates; - -namespace Microsoft.AspNetCore -{ - internal interface ICertificateFileLoader - { - X509Certificate2 Load(string path, string password, X509KeyStorageFlags flags); - } -} diff --git a/src/Microsoft.AspNetCore/ICertificateStoreLoader.cs b/src/Microsoft.AspNetCore/ICertificateStoreLoader.cs deleted file mode 100644 index f128bbd675..0000000000 --- a/src/Microsoft.AspNetCore/ICertificateStoreLoader.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Security.Cryptography.X509Certificates; - -namespace Microsoft.AspNetCore -{ - internal interface ICertificateStoreLoader - { - X509Certificate2 Load(string subject, string storeName, StoreLocation storeLocation, bool validOnly); - } -} diff --git a/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs b/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs index c36bf2f59a..a2a5fe1ca2 100644 --- a/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs +++ b/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNetCore.Certificates.Configuration; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; diff --git a/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj b/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj index 153156b71e..cf888d57ca 100644 --- a/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj +++ b/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj @@ -3,7 +3,7 @@ - netcoreapp2.0;net461 + netstandard2.0 aspnetcore Microsoft.AspNetCore true diff --git a/src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs deleted file mode 100644 index a3a00f9d5c..0000000000 --- a/src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs deleted file mode 100644 index 1db8765e82..0000000000 --- a/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs +++ /dev/null @@ -1,1044 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.FunctionalTests -{ - public class CertificateLoaderTests - { - [Fact] - public void Loads_SingleCertificateName_File() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["Certificates:Certificate1:Source"] = "File", - ["Certificates:Certificate1:Path"] = "Certificate1.pfx", - ["Certificates:Certificate1:Password"] = "Password1", - ["TestConfig:Certificate"] = "Certificate1" - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(certificate); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(1, loadedCertificates.Count()); - Assert.Same(certificate, loadedCertificates.ElementAt(0)); - certificateFileLoader.VerifyAll(); - } - - [Fact] - public void Throws_SingleCertificateName_KeyNotFound() - { - 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 Throws_SingleCertificateName_File_FileLoadError() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["Certificates:Certificate1:Source"] = "File", - ["Certificates:Certificate1:Path"] = "Certificate1.pfx", - ["Certificates:Certificate1:Password"] = "Password1", - ["TestConfig:Certificate"] = "Certificate1" - }) - .Build(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Callback(() => throw new Exception(nameof(Throws_SingleCertificateName_File_FileLoadError))); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - 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] - public void Loads_SingleCertificateName_Store() - { - 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"] = "Certificate1" - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(certificate); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(1, loadedCertificates.Count()); - Assert.Same(certificate, loadedCertificates.ElementAt(0)); - certificateStoreLoader.VerifyAll(); - } - - [Fact] - public void ReturnsNull_SingleCertificateName_Store_NotFoundInStore() - { - 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"] = "Certificate1" - }) - .Build(); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(null); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(0, loadedCertificates.Count()); - certificateStoreLoader.VerifyAll(); - } - - [Fact] - public void Loads_MultipleCertificateNames_File() - { - 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"] = "Certificate1;Certificate2" - }) - .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"), - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(2, loadedCertificates.Count()); - Assert.Same(certificate1, loadedCertificates.ElementAt(0)); - Assert.Same(certificate2, loadedCertificates.ElementAt(1)); - certificateFileLoader.VerifyAll(); - } - - [Theory] - [InlineData("Certificate1;Certificate2")] - [InlineData("Certificate2;Certificate1")] - public void Throws_MultipleCertificateNames_File_FileLoadError(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 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())) - .Throws(new Exception(nameof(Throws_MultipleCertificateNames_File_FileLoadError))); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - 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] - public void Loads_MultipleCertificateNames_Store() - { - 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"] = "Certificate1;Certificate2" - }) - .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"), - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(2, loadedCertificates.Count()); - Assert.Same(certificate1, loadedCertificates.ElementAt(0)); - Assert.Same(certificate2, loadedCertificates.ElementAt(1)); - certificateStoreLoader.VerifyAll(); - } - - [Theory] - [InlineData("Certificate1;Certificate2", 1)] - [InlineData("Certificate2;Certificate1", 1)] - [InlineData("Certificate1;Certificate2;Certificate3", 1)] - [InlineData("Certificate2;Certificate3", 0)] - [InlineData("Certificate2;Certificate3;Certificate1", 1)] - public void ReturnsNull_MultipleCertificateNames_Store_NotFoundInStore(string certificateNames, int expectedFoundCertificates) - { - 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", - ["Certificates:Certificate3:Source"] = "Store", - ["Certificates:Certificate3:Subject"] = "notfound.com", - ["Certificates:Certificate3:StoreName"] = "Root", - ["Certificates:Certificate3:StoreLocation"] = "LocalMachine", - ["TestConfig:Certificate"] = certificateNames - }) - .Build(); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(new X509Certificate2()); - certificateStoreLoader - .Setup(loader => loader.Load("example.com", "Root", StoreLocation.LocalMachine, It.IsAny())) - .Returns(null); - certificateStoreLoader - .Setup(loader => loader.Load("notfound.com", "Root", StoreLocation.LocalMachine, It.IsAny())) - .Returns(null); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(expectedFoundCertificates, loadedCertificates.Count()); - } - - [Fact] - public void Loads_MultipleCertificateNames_FileAndStore() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["Certificates:Certificate1:Source"] = "File", - ["Certificates:Certificate1:Path"] = "Certificate1.pfx", - ["Certificates:Certificate1:Password"] = "Password1", - ["Certificates:Certificate2:Source"] = "Store", - ["Certificates:Certificate2:Subject"] = "localhost", - ["Certificates:Certificate2:StoreName"] = "My", - ["Certificates:Certificate2:StoreLocation"] = "CurrentUser", - ["TestConfig:Certificate"] = "Certificate1;Certificate2" - }) - .Build(); - - var fileCertificate = new X509Certificate2(); - var storeCertificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(fileCertificate); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(storeCertificate); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - certificateFileLoader.Object, - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(2, loadedCertificates.Count()); - Assert.Same(fileCertificate, loadedCertificates.ElementAt(0)); - Assert.Same(storeCertificate, loadedCertificates.ElementAt(1)); - certificateFileLoader.VerifyAll(); - certificateStoreLoader.VerifyAll(); - } - - [Theory] - [InlineData("Certificate1;Certificate2;NotFound")] - [InlineData("Certificate1;NotFound;Certificate2")] - [InlineData("NotFound;Certificate1;Certificate2")] - public void Throws_MultipleCertificateNames_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"] = "Store", - ["Certificates:Certificate2:Subject"] = "localhost", - ["Certificates:Certificate2:StoreName"] = "My", - ["Certificates:Certificate2:StoreLocation"] = "CurrentUser", - ["TestConfig:Certificate"] = certificateNames - }) - .Build(); - - var fileCertificate = new X509Certificate2(); - var storeCertificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(fileCertificate); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(storeCertificate); - - 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 for the current environment.", exception.Message); - } - - [Theory] - [InlineData("Certificate1;Certificate2")] - [InlineData("Certificate2;Certificate1")] - public void Throws_MultipleCertificateNames_FileAndStore_FileLoadError(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"] = "Store", - ["Certificates:Certificate2:Subject"] = "localhost", - ["Certificates:Certificate2:StoreName"] = "My", - ["Certificates:Certificate2:StoreLocation"] = "CurrentUser", - ["TestConfig:Certificate"] = certificateNames - }) - .Build(); - - var storeCertificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Throws(new Exception(nameof(Throws_MultipleCertificateNames_FileAndStore_FileLoadError))); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(storeCertificate); - - 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($"Unable to load certificate from file 'Certificate1.pfx'. Error details: '{nameof(Throws_MultipleCertificateNames_FileAndStore_FileLoadError)}'.", exception.Message); - } - - [Theory] - [InlineData("Certificate1;Certificate2")] - [InlineData("Certificate2;Certificate1")] - public void ReturnsNull_MultipleCertificateNames_FileAndStore_NotFoundInStore(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"] = "Store", - ["Certificates:Certificate2:Subject"] = "localhost", - ["Certificates:Certificate2:StoreName"] = "My", - ["Certificates:Certificate2:StoreLocation"] = "CurrentUser", - ["TestConfig:Certificate"] = "Certificate1;Certificate2" - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(certificate); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(null); - - var certificateLoader = new CertificateLoader( - configuration.GetSection("Certificates"), - null, - null, - certificateFileLoader.Object, - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(1, loadedCertificates.Count()); - Assert.Same(certificate, loadedCertificates.ElementAt(0)); - certificateFileLoader.VerifyAll(); - certificateStoreLoader.VerifyAll(); - } - - [Fact] - public void Loads_SingleCertificateInline_File() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificate:Source"] = "File", - ["TestConfig:Certificate:Path"] = "Certificate1.pfx", - ["TestConfig:Certificate:Password"] = "Password1" - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(certificate); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(1, loadedCertificates.Count()); - Assert.Same(certificate, loadedCertificates.ElementAt(0)); - certificateFileLoader.VerifyAll(); - } - - [Fact] - public void Throws_SingleCertificateInline_FileLoadError() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificate:Source"] = "File", - ["TestConfig:Certificate:Path"] = "Certificate1.pfx", - ["TestConfig:Certificate:Password"] = "Password1" - }) - .Build(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Throws(new Exception(nameof(Throws_SingleCertificateInline_FileLoadError))); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - 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(); - } - - [Fact] - public void Loads_SingleCertificateInline_Store() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificate:Source"] = "Store", - ["TestConfig:Certificate:Subject"] = "localhost", - ["TestConfig:Certificate:StoreName"] = "My", - ["TestConfig:Certificate:StoreLocation"] = "CurrentUser", - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(certificate); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(1, loadedCertificates.Count()); - Assert.Same(certificate, loadedCertificates.ElementAt(0)); - certificateStoreLoader.VerifyAll(); - } - - [Fact] - public void ReturnsNull_SingleCertificateInline_Store_NotFoundInStore() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificate:Source"] = "Store", - ["TestConfig:Certificate:Subject"] = "localhost", - ["TestConfig:Certificate:StoreName"] = "My", - ["TestConfig:Certificate:StoreLocation"] = "CurrentUser", - }) - .Build(); - - var certificateStoreLoader = new Mock(); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificate")); - Assert.Equal(0, loadedCertificates.Count()); - certificateStoreLoader.Verify(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())); - } - - [Fact] - public void Loads_MultipleCertificatesInline_File() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificates:Certificate1:Source"] = "File", - ["TestConfig:Certificates:Certificate1:Path"] = "Certificate1.pfx", - ["TestConfig:Certificates:Certificate1:Password"] = "Password1", - ["TestConfig:Certificates:Certificate2:Source"] = "File", - ["TestConfig:Certificates:Certificate2:Path"] = "Certificate2.pfx", - ["TestConfig:Certificates:Certificate2:Password"] = "Password2", - }) - .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( - null, - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")); - Assert.Equal(2, loadedCertificates.Count()); - Assert.Same(certificate1, loadedCertificates.ElementAt(0)); - Assert.Same(certificate2, loadedCertificates.ElementAt(1)); - certificateFileLoader.VerifyAll(); - } - - [Fact] - public void Throws_MultipleCertificatesInline_File_FileLoadError() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificates:Certificate1:Source"] = "File", - ["TestConfig:Certificates:Certificate1:Path"] = "Certificate1.pfx", - ["TestConfig:Certificates:Certificate1:Password"] = "Password1", - ["TestConfig:Certificates:Certificate2:Source"] = "File", - ["TestConfig:Certificates:Certificate2:Path"] = "Certificate2.pfx", - ["TestConfig:Certificates:Certificate2:Password"] = "Password2", - }) - .Build(); - - var certificate1 = 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())) - .Throws(new Exception(nameof(Throws_MultipleCertificatesInline_File_FileLoadError))); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - certificateFileLoader.Object, - Mock.Of()); - - 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] - public void Loads_MultipleCertificatesInline_Store() - { - 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", - ["TestConfig:Certificates:Certificate2:Source"] = "Store", - ["TestConfig:Certificates:Certificate2:Subject"] = "example.com", - ["TestConfig:Certificates:Certificate2:StoreName"] = "Root", - ["TestConfig:Certificates:Certificate2:StoreLocation"] = "LocalMachine" - }) - .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( - null, - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")); - Assert.Equal(2, loadedCertificates.Count()); - Assert.Same(certificate1, loadedCertificates.ElementAt(0)); - Assert.Same(certificate2, loadedCertificates.ElementAt(1)); - certificateStoreLoader.VerifyAll(); - } - - [Fact] - public void ReturnsNull_MultipleCertificatesInline_Store_NotFoundInStore() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["TestConfig:Certificates:Certificate1:Source"] = "Store", - ["TestConfig:Certificates:Certificate1:Subject"] = "notfound.com", - ["TestConfig:Certificates:Certificate1:StoreName"] = "Root", - ["TestConfig:Certificates:Certificate1:StoreLocation"] = "LocalMachine", - ["TestConfig:Certificates:Certificate2:Source"] = "Store", - ["TestConfig:Certificates:Certificate2:Subject"] = "localhost", - ["TestConfig:Certificates:Certificate2:StoreName"] = "My", - ["TestConfig:Certificates:Certificate2:StoreLocation"] = "CurrentUser", - ["TestConfig:Certificates:Certificate3:Source"] = "Store", - ["TestConfig:Certificates:Certificate3:Subject"] = "example.com", - ["TestConfig:Certificates:Certificate3:StoreName"] = "Root", - ["TestConfig:Certificates:Certificate3:StoreLocation"] = "LocalMachine" - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(certificate); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - Mock.Of(), - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")); - Assert.Equal(1, loadedCertificates.Count()); - Assert.Same(certificate, loadedCertificates.ElementAt(0)); - certificateStoreLoader.Verify(loader => loader.Load("notfound.com", "Root", StoreLocation.LocalMachine, It.IsAny())); - certificateStoreLoader.Verify(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())); - certificateStoreLoader.Verify(loader => loader.Load("example.com", "Root", StoreLocation.LocalMachine, It.IsAny())); - } - - [Fact] - public void Loads_MultipleCertificatesInline_FileAndStore() - { - 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", - ["TestConfig:Certificates:Certificate2:Source"] = "File", - ["TestConfig:Certificates:Certificate2:Path"] = "Certificate1.pfx", - ["TestConfig:Certificates:Certificate2:Password"] = "Password1", - ["TestConfig:Certificates:Certificate3:Source"] = "Store", - ["TestConfig:Certificates:Certificate3:Subject"] = "example.com", - ["TestConfig:Certificates:Certificate3:StoreName"] = "Root", - ["TestConfig:Certificates:Certificate3:StoreLocation"] = "LocalMachine", - ["TestConfig:Certificates:Certificate4:Source"] = "File", - ["TestConfig:Certificates:Certificate4:Path"] = "Certificate2.pfx", - ["TestConfig:Certificates:Certificate4:Password"] = "Password2", - }) - .Build(); - - var fileCertificate1 = new X509Certificate2(); - var fileCertificate2 = new X509Certificate2(); - var storeCertificate1 = new X509Certificate2(); - var storeCertificate2 = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(fileCertificate1); - certificateFileLoader - .Setup(loader => loader.Load("Certificate2.pfx", "Password2", It.IsAny())) - .Returns(fileCertificate2); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(storeCertificate1); - certificateStoreLoader - .Setup(loader => loader.Load("example.com", "Root", StoreLocation.LocalMachine, It.IsAny())) - .Returns(storeCertificate2); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - certificateFileLoader.Object, - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")); - Assert.Equal(4, loadedCertificates.Count()); - Assert.Same(storeCertificate1, loadedCertificates.ElementAt(0)); - Assert.Same(fileCertificate1, loadedCertificates.ElementAt(1)); - Assert.Same(storeCertificate2, loadedCertificates.ElementAt(2)); - Assert.Same(fileCertificate2, loadedCertificates.ElementAt(3)); - certificateStoreLoader.VerifyAll(); - } - - [Fact] - public void Throws_MultipleCertificatesInline_FileAndStore_FileLoadError() - { - 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", - ["TestConfig:Certificates:Certificate2:Source"] = "File", - ["TestConfig:Certificates:Certificate2:Path"] = "Certificate1.pfx", - ["TestConfig:Certificates:Certificate2:Password"] = "Password1", - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Throws(new Exception(nameof(Throws_MultipleCertificatesInline_FileAndStore_FileLoadError))); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(certificate); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - certificateFileLoader.Object, - certificateStoreLoader.Object); - - 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] - public void ReturnsNull_MultipleCertificatesInline_FileAndStore_NotFoundInStore() - { - 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", - ["TestConfig:Certificates:Certificate2:Source"] = "File", - ["TestConfig:Certificates:Certificate2:Path"] = "Certificate1.pfx", - ["TestConfig:Certificates:Certificate2:Password"] = "Password1", - }) - .Build(); - - var certificate = new X509Certificate2(); - - var certificateFileLoader = new Mock(); - certificateFileLoader - .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny())) - .Returns(certificate); - - var certificateStoreLoader = new Mock(); - certificateStoreLoader - .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny())) - .Returns(null); - - var certificateLoader = new CertificateLoader( - null, - null, - null, - certificateFileLoader.Object, - certificateStoreLoader.Object); - - var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates")); - 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; } - } - } - } -}