diff --git a/MetaPackages.sln b/MetaPackages.sln
index 19e4629..cfcc774 100644
--- a/MetaPackages.sln
+++ b/MetaPackages.sln
@@ -40,11 +40,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StartRequestDelegateUrlApp"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateDefaultBuilderApp", "test\TestSites\CreateDefaultBuilderApp\CreateDefaultBuilderApp.csproj", "{79CF58CE-B020-45D8-BDB5-2D8036BEAD14}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestArtifacts", "TestArtifacts", "{9BBA7A0A-109A-4AC8-B6EF-A52EA7CF1D90}"
- ProjectSection(SolutionItems) = preProject
- test\TestArtifacts\testCert.pfx = test\TestArtifacts\testCert.pfx
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-archive", "src\dotnet-archive\dotnet-archive.csproj", "{AE4216BF-D471-471B-82F3-6B6D004F7D17}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Archive", "src\Microsoft.DotNet.Archive\Microsoft.DotNet.Archive.csproj", "{302400A0-98BB-4C04-88D4-C32DC2D4B945}"
@@ -121,7 +116,6 @@ Global
{3A85FA52-F601-422E-A42E-9F187DB28492} = {EC22261D-0DE1-47DE-8F7C-072675D6F5B4}
{401C741B-6C7C-4E08-9F09-C3D43D22C0DE} = {EC22261D-0DE1-47DE-8F7C-072675D6F5B4}
{79CF58CE-B020-45D8-BDB5-2D8036BEAD14} = {EC22261D-0DE1-47DE-8F7C-072675D6F5B4}
- {9BBA7A0A-109A-4AC8-B6EF-A52EA7CF1D90} = {9E49B5B9-9E72-42FB-B684-90CA1B1BCF9C}
{AE4216BF-D471-471B-82F3-6B6D004F7D17} = {ED834E68-51C3-4ADE-ACC8-6BA6D4207C09}
{302400A0-98BB-4C04-88D4-C32DC2D4B945} = {ED834E68-51C3-4ADE-ACC8-6BA6D4207C09}
{67E4C92F-6D12-4C52-BB79-B8D11BFC6B82} = {ED834E68-51C3-4ADE-ACC8-6BA6D4207C09}
diff --git a/build/dependencies.props b/build/dependencies.props
index caea34a..e532f8a 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -5,6 +5,7 @@
1.0.0-*
4.3.0
2.0.0-*
+ 4.7.1
10.0.1
15.0.0
2.2.0
diff --git a/samples/AppSettings/appsettings.json b/samples/AppSettings/appsettings.json
index da72535..fac810e 100644
--- a/samples/AppSettings/appsettings.json
+++ b/samples/AppSettings/appsettings.json
@@ -28,7 +28,7 @@
"Source": "File",
"Path": "testCert.pfx",
// TODO: remove when dotnet user-secrets is working again
- "Password": "testPassword",
+ "Password": "testPassword"
}
},
// Add testCert.pfx to the current user's certificate store to enable this scenario.
diff --git a/src/Microsoft.AspNetCore/CertificateFileLoader.cs b/src/Microsoft.AspNetCore/CertificateFileLoader.cs
new file mode 100644
index 0000000..1c0b6e5
--- /dev/null
+++ b/src/Microsoft.AspNetCore/CertificateFileLoader.cs
@@ -0,0 +1,15 @@
+// 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
index 9533904..be006b4 100644
--- a/src/Microsoft.AspNetCore/CertificateLoader.cs
+++ b/src/Microsoft.AspNetCore/CertificateLoader.cs
@@ -12,49 +12,125 @@ namespace Microsoft.AspNetCore
///
/// A helper class to load certificates from files and certificate stores based on data.
///
- public static class CertificateLoader
+ public class CertificateLoader
{
+ private readonly IConfiguration _certificatesConfiguration;
+ private readonly ICertificateFileLoader _certificateFileLoader;
+ private readonly ICertificateStoreLoader _certificateStoreLoader;
+
+ ///
+ /// Creates a new instance of .
+ ///
+ public CertificateLoader()
+ : this(null)
+ {
+ }
+
+ ///
+ /// 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())
+ {
+ _certificatesConfiguration = certificatesConfiguration;
+ }
+
+ internal CertificateLoader(IConfiguration certificatesConfiguration, ICertificateFileLoader certificateFileLoader, ICertificateStoreLoader certificateStoreLoader)
+ {
+ _certificatesConfiguration = certificatesConfiguration;
+ _certificateFileLoader = certificateFileLoader;
+ _certificateStoreLoader = certificateStoreLoader;
+ }
+
+ ///
+ /// 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 one or more certificates from a single source.
+ /// Loads a certificate by name.
///
- /// An with information about a certificate source.
- /// The certificate password, in case it's being loaded from a file.
- /// The loaded certificates.
- public static X509Certificate2 Load(IConfiguration certificateConfiguration, string password)
+ /// 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())
+ {
+ throw new InvalidOperationException($"No certificate named {certificateName} found in configuration");
+ }
+
+ return LoadSingle(certificateConfiguration);
+ }
+
+ private X509Certificate2 LoadSingle(IConfigurationSection certificateConfiguration)
{
- var sourceKind = certificateConfiguration.GetValue("Source");
+ var sourceKind = certificateConfiguration["Source"];
CertificateSource certificateSource;
switch (sourceKind.ToLowerInvariant())
{
case "file":
- certificateSource = new CertificateFileSource(password);
+ certificateSource = new CertificateFileSource(_certificateFileLoader);
break;
case "store":
- certificateSource = new CertificateStoreSource();
+ certificateSource = new CertificateStoreSource(_certificateStoreLoader);
break;
default:
throw new InvalidOperationException($"Invalid certificate source kind: {sourceKind}");
}
certificateConfiguration.Bind(certificateSource);
+
return certificateSource.Load();
}
- ///
- /// Loads all certificates specified in an .
- ///
- /// The root .
- ///
- /// A dictionary mapping certificate names to loaded certificates.
- ///
- public static Dictionary LoadAll(IConfiguration configurationRoot)
- {
- return configurationRoot.GetSection("Certificates").GetChildren()
- .ToDictionary(
- certificateSource => certificateSource.Key,
- certificateSource => Load(certificateSource, certificateSource["Password"]));
- }
+ private IEnumerable LoadMultiple(IConfigurationSection certificatesConfiguration)
+ => certificatesConfiguration.GetChildren()
+ .Select(LoadSingle)
+ .Where(c => c != null);
private abstract class CertificateSource
{
@@ -65,22 +141,24 @@ private abstract class CertificateSource
private class CertificateFileSource : CertificateSource
{
- private readonly string _password;
+ private ICertificateFileLoader _certificateFileLoader;
- public CertificateFileSource(string password)
+ public CertificateFileSource(ICertificateFileLoader certificateFileLoader)
{
- _password = password;
+ _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
+#if NETCOREAPP2_0
?? TryLoad(X509KeyStorageFlags.EphemeralKeySet, out error)
- #endif
+#endif
;
if (error != null)
@@ -95,7 +173,7 @@ private X509Certificate2 TryLoad(X509KeyStorageFlags flags, out Exception except
{
try
{
- var loadedCertificate = new X509Certificate2(Path, _password, flags);
+ var loadedCertificate = _certificateFileLoader.Load(Path, Password, flags);
exception = null;
return loadedCertificate;
}
@@ -109,6 +187,13 @@ private X509Certificate2 TryLoad(X509KeyStorageFlags flags, out Exception except
private class CertificateStoreSource : CertificateSource
{
+ private readonly ICertificateStoreLoader _certificateStoreLoader;
+
+ public CertificateStoreSource(ICertificateStoreLoader certificateStoreLoader)
+ {
+ _certificateStoreLoader = certificateStoreLoader;
+ }
+
public string Subject { get; set; }
public string StoreName { get; set; }
public string StoreLocation { get; set; }
@@ -121,52 +206,7 @@ public override X509Certificate2 Load()
throw new InvalidOperationException($"Invalid store location: {StoreLocation}");
}
- 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: !AllowInvalid);
- foundCertificate = foundCertificates
- .OfType()
- .OrderByDescending(certificate => certificate.NotAfter)
- .FirstOrDefault();
-
- if (foundCertificate == null)
- {
- throw new InvalidOperationException($"No certificate found for {Subject} in store {StoreName} in {StoreLocation}");
- }
-
- 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();
- }
- }
+ return _certificateStoreLoader.Load(Subject, StoreName, storeLocation, !AllowInvalid);
}
}
}
diff --git a/src/Microsoft.AspNetCore/CertificateStoreLoader.cs b/src/Microsoft.AspNetCore/CertificateStoreLoader.cs
new file mode 100644
index 0000000..27158d4
--- /dev/null
+++ b/src/Microsoft.AspNetCore/CertificateStoreLoader.cs
@@ -0,0 +1,56 @@
+// 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
new file mode 100644
index 0000000..4c394bb
--- /dev/null
+++ b/src/Microsoft.AspNetCore/ICertificateFileLoader.cs
@@ -0,0 +1,12 @@
+// 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
new file mode 100644
index 0000000..f128bbd
--- /dev/null
+++ b/src/Microsoft.AspNetCore/ICertificateStoreLoader.cs
@@ -0,0 +1,12 @@
+// 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 888ece3..d2ec6f8 100644
--- a/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs
+++ b/src/Microsoft.AspNetCore/KestrelServerOptionsSetup.cs
@@ -2,39 +2,24 @@
// 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.Net;
-using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
-using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore
{
- ///
- /// Binds Kestrel configuration.
- ///
- public class KestrelServerOptionsSetup : IConfigureOptions
+ internal class KestrelServerOptionsSetup : IConfigureOptions
{
private readonly IConfiguration _configurationRoot;
- ///
- /// Creates a new instance of .
- ///
- /// The root .
public KestrelServerOptionsSetup(IConfiguration configurationRoot)
{
_configurationRoot = configurationRoot;
}
- ///
- /// Configures a instance.
- ///
- /// The to configure.
public void Configure(KestrelServerOptions options)
{
BindConfiguration(options);
@@ -42,60 +27,46 @@ public void Configure(KestrelServerOptions options)
private void BindConfiguration(KestrelServerOptions options)
{
- var certificates = CertificateLoader.LoadAll(_configurationRoot);
- var endPoints = _configurationRoot.GetSection("Kestrel:EndPoints");
+ var certificateLoader = new CertificateLoader(_configurationRoot.GetSection("Certificates"));
- foreach (var endPoint in endPoints.GetChildren())
+ foreach (var endPoint in _configurationRoot.GetSection("Kestrel:EndPoints").GetChildren())
{
- BindEndPoint(options, endPoint, certificates);
+ BindEndPoint(options, endPoint, certificateLoader);
}
}
private void BindEndPoint(
KestrelServerOptions options,
IConfigurationSection endPoint,
- Dictionary certificates)
+ CertificateLoader certificateLoader)
{
- var addressValue = endPoint.GetValue("Address");
- var portValue = endPoint.GetValue("Port");
+ var configAddress = endPoint.GetValue("Address");
+ var configPort = endPoint.GetValue("Port");
- IPAddress address;
- if (!IPAddress.TryParse(addressValue, out address))
+ if (!IPAddress.TryParse(configAddress, out var address))
{
- throw new InvalidOperationException($"Invalid IP address: {addressValue}");
+ throw new InvalidOperationException($"Invalid IP address in configuration: {configAddress}");
}
- int port;
- if (!int.TryParse(portValue, out port))
+ if (!int.TryParse(configPort, out var port))
{
- throw new InvalidOperationException($"Invalid port: {portValue}");
+ throw new InvalidOperationException($"Invalid port in configuration: {configPort}");
}
options.Listen(address, port, listenOptions =>
{
- var certificateName = endPoint.GetValue("Certificate");
+ var certificateConfig = endPoint.GetSection("Certificate");
- X509Certificate2 endPointCertificate = null;
- if (certificateName != null)
+ if (certificateConfig.Exists())
{
- if (!certificates.TryGetValue(certificateName, out endPointCertificate))
- {
- throw new InvalidOperationException($"No certificate named {certificateName} found in configuration");
- }
- }
- else
- {
- var certificate = endPoint.GetSection("Certificate");
+ var certificate = certificateLoader.Load(certificateConfig).FirstOrDefault();
- if (certificate.GetChildren().Any())
+ if (certificate == null)
{
- endPointCertificate = CertificateLoader.Load(certificate, certificate["Password"]);
+ throw new InvalidOperationException($"Unable to load certificate for endpoint '{endPoint.Key}'");
}
- }
- if (endPointCertificate != null)
- {
- listenOptions.UseHttps(endPointCertificate);
+ listenOptions.UseHttps(certificate);
}
});
}
diff --git a/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj b/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj
index 5350bf9..5841f9d 100644
--- a/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj
+++ b/src/Microsoft.AspNetCore/Microsoft.AspNetCore.csproj
@@ -3,7 +3,7 @@
- netstandard1.3
+ netstandard1.3;netcoreapp2.0
aspnetcore
Microsoft.AspNetCore
true
diff --git a/src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..a3a00f9
--- /dev/null
+++ b/src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs
@@ -0,0 +1,7 @@
+// 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
new file mode 100644
index 0000000..7cf3787
--- /dev/null
+++ b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs
@@ -0,0 +1,999 @@
+// 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 Xunit;
+using Moq;
+
+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"),
+ 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_File_KeyNotFound()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["Certificates:Certificate1:Source"] = "File",
+ ["Certificates:Certificate1:Path"] = "Certificate1.pfx",
+ ["Certificates:Certificate1:Password"] = "Password1",
+ ["TestConfig:Certificate"] = "Certificate2"
+ })
+ .Build();
+
+ var certificate = new X509Certificate2();
+
+ 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 Throws_SingleCertificateName_File_FileNotFound()
+ {
+ 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 exception = new Exception();
+
+ var certificateFileLoader = new Mock();
+ certificateFileLoader
+ .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
+ .Callback(() => throw exception);
+
+ var certificateLoader = new CertificateLoader(
+ configuration.GetSection("Certificates"),
+ certificateFileLoader.Object,
+ Mock.Of());
+
+ Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ }
+
+ [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"),
+ 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 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()
+ {
+ 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"),
+ 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"),
+ 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;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)
+ {
+ 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 exception = new Exception();
+
+ 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(exception);
+
+ var certificateLoader = new CertificateLoader(
+ configuration.GetSection("Certificates"),
+ certificateFileLoader.Object,
+ Mock.Of());
+
+ Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ }
+
+ [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"),
+ 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;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)]
+ [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"),
+ 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"),
+ 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_FileAndStore_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"),
+ 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);
+ }
+
+ [Theory]
+ [InlineData("Certificate1;Certificate2")]
+ [InlineData("Certificate2;Certificate1")]
+ public void Throws_MultipleCertificateNames_FileAndStore_FileNotFound(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 exception = new Exception();
+ var storeCertificate = new X509Certificate2();
+
+ var certificateFileLoader = new Mock();
+ certificateFileLoader
+ .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
+ .Throws(exception);
+
+ var certificateStoreLoader = new Mock();
+ certificateStoreLoader
+ .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny()))
+ .Returns(storeCertificate);
+
+ var certificateLoader = new CertificateLoader(
+ configuration.GetSection("Certificates"),
+ certificateFileLoader.Object,
+ certificateStoreLoader.Object);
+
+ Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ }
+
+ [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"),
+ 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,
+ 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_FileNotFound()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["TestConfig:Certificate:Source"] = "File",
+ ["TestConfig:Certificate:Path"] = "Certificate1.pfx",
+ ["TestConfig:Certificate:Password"] = "Password1"
+ })
+ .Build();
+
+ var exception = new Exception();
+
+ var certificateFileLoader = new Mock();
+ certificateFileLoader
+ .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
+ .Throws(exception);
+
+ var certificateLoader = new CertificateLoader(
+ null,
+ certificateFileLoader.Object,
+ Mock.Of());
+
+ Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificate"))));
+ 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,
+ 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,
+ 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,
+ 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_FileNotFound()
+ {
+ 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 exception = new Exception();
+
+ 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(exception);
+
+ var certificateLoader = new CertificateLoader(
+ null,
+ certificateFileLoader.Object,
+ Mock.Of());
+
+ Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificates"))));
+ }
+
+ [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,
+ 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,
+ 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,
+ 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_FileNotFound()
+ {
+ 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 exception = new Exception();
+ var certificate = new X509Certificate2();
+
+ var certificateFileLoader = new Mock();
+ certificateFileLoader
+ .Setup(loader => loader.Load("Certificate1.pfx", "Password1", It.IsAny()))
+ .Throws(exception);
+
+ var certificateStoreLoader = new Mock();
+ certificateStoreLoader
+ .Setup(loader => loader.Load("localhost", "My", StoreLocation.CurrentUser, It.IsAny()))
+ .Returns(certificate);
+
+ var certificateLoader = new CertificateLoader(
+ null,
+ certificateFileLoader.Object,
+ certificateStoreLoader.Object);
+
+ Assert.Same(exception, Assert.Throws(() => certificateLoader.Load(configuration.GetSection("TestConfig:Certificates"))));
+ }
+
+ [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,
+ certificateFileLoader.Object,
+ certificateStoreLoader.Object);
+
+ var loadedCertificates = certificateLoader.Load(configuration.GetSection("TestConfig:Certificates"));
+ Assert.Equal(1, loadedCertificates.Count());
+ Assert.Same(certificate, loadedCertificates.ElementAt(0));
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj
index a4676dc..13d57e6 100644
--- a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj
+++ b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj
@@ -7,7 +7,7 @@
-
+
@@ -17,6 +17,7 @@
+
diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx
new file mode 100644
index 0000000..c792c52
Binary files /dev/null and b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx differ
diff --git a/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs b/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs
index 43be04f..00e1fbf 100644
--- a/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs
+++ b/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs
@@ -134,8 +134,7 @@ public async Task BindsKestrelHttpsEndPointFromConfiguration_ReferencedCertifica
""Certificates"": {
""TestCert"": {
""Source"": ""File"",
- ""Path"": ""testCert.pfx"",
- ""Password"": ""testPassword""
+ ""Path"": ""TestArtifacts/Certificate.pfx""
}
}
}
@@ -173,8 +172,7 @@ public async Task BindsKestrelHttpsEndPointFromConfiguration_InlineCertificateFi
""Port"": 0,
""Certificate"": {
""Source"": ""File"",
- ""Path"": ""testCert.pfx"",
- ""Password"": ""testPassword""
+ ""Path"": ""TestArtifacts/Certificate.pfx"",
}
}
}
diff --git a/test/TestArtifacts/testCert.pfx b/test/TestArtifacts/testCert.pfx
deleted file mode 100644
index 7118908..0000000
Binary files a/test/TestArtifacts/testCert.pfx and /dev/null differ