From b41e7fa37d71e07566e803edce43ea068939dcb5 Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Fri, 28 Apr 2017 14:24:28 -0700 Subject: [PATCH 1/5] Support more certificate loading scenarios (#69). --- MetaPackages.sln | 6 - src/Microsoft.AspNetCore/CertificateLoader.cs | 111 +++++++++++----- .../KestrelServerOptionsSetup.cs | 63 +++------- .../CertificateLoaderTests.cs | 118 ++++++++++++++++++ ...icrosoft.AspNetCore.FunctionalTests.csproj | 7 +- .../TestArtifacts/Certificate.pfx | Bin 0 -> 2461 bytes .../TestArtifacts/Certificate1.pfx | Bin 0 -> 2461 bytes .../TestArtifacts/Certificate2.pfx | Bin 0 -> 2461 bytes .../TestArtifacts/Certificate3.pfx | Bin 0 -> 2461 bytes .../TestArtifacts/Certificate4.pfx | Bin 0 -> 2461 bytes .../TestArtifacts/Certificate5.pfx | Bin 0 -> 2461 bytes .../WebHostFunctionalTests.cs | 6 +- test/TestArtifacts/testCert.pfx | Bin 2483 -> 0 bytes 13 files changed, 224 insertions(+), 87 deletions(-) create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate1.pfx create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate2.pfx create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate3.pfx create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate4.pfx create mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate5.pfx delete mode 100644 test/TestArtifacts/testCert.pfx 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/src/Microsoft.AspNetCore/CertificateLoader.cs b/src/Microsoft.AspNetCore/CertificateLoader.cs index 9533904..550cd45 100644 --- a/src/Microsoft.AspNetCore/CertificateLoader.cs +++ b/src/Microsoft.AspNetCore/CertificateLoader.cs @@ -12,23 +12,90 @@ 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; + + /// + /// Creates a new instance of . + /// + public CertificateLoader() + { + } + + /// + /// Creates a new instance of that can load certificates by name. + /// + /// An instance with information about certificates. + public CertificateLoader(IConfiguration certificatesConfig) + { + _certificatesConfiguration = certificatesConfig; + } + + /// + /// 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; + + List certificates = new List(); + + if (certificateNames != null) + { + foreach (var certificateName in certificateNames.Split(' ')) + { + certificates.Add(Load(certificateName)); + } + } + else + { + if (certificateConfiguration["Source"] != null) + { + certificates.Add(LoadSingle(certificateConfiguration)); + } + 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. + /// + public X509Certificate2 Load(string certificateName) { - var sourceKind = certificateConfiguration.GetValue("Source"); + var certificateConfiguration = _certificatesConfiguration?.GetSection(certificateName); + + if (certificateConfiguration == null) + { + throw new InvalidOperationException($"No certificate named {certificateName} found in configuration"); + } + + return LoadSingle(certificateConfiguration); + } + + private X509Certificate2 LoadSingle(IConfigurationSection certificateConfiguration) + { + var sourceKind = certificateConfiguration["Source"]; CertificateSource certificateSource; switch (sourceKind.ToLowerInvariant()) { case "file": - certificateSource = new CertificateFileSource(password); + certificateSource = new CertificateFileSource(); break; case "store": certificateSource = new CertificateStoreSource(); @@ -38,23 +105,12 @@ public static X509Certificate2 Load(IConfiguration certificateConfiguration, str } 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); private abstract class CertificateSource { @@ -65,15 +121,10 @@ private abstract class CertificateSource private class CertificateFileSource : CertificateSource { - private readonly string _password; - - public CertificateFileSource(string password) - { - _password = password; - } - public string Path { get; set; } + public string Password { get; set; } + public override X509Certificate2 Load() { var certificate = TryLoad(X509KeyStorageFlags.DefaultKeySet, out var error) @@ -95,7 +146,7 @@ private X509Certificate2 TryLoad(X509KeyStorageFlags flags, out Exception except { try { - var loadedCertificate = new X509Certificate2(Path, _password, flags); + var loadedCertificate = new X509Certificate2(Path, Password, flags); exception = null; return loadedCertificate; } 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/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs new file mode 100644 index 0000000..fad12e4 --- /dev/null +++ b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs @@ -0,0 +1,118 @@ +// 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; + +namespace Microsoft.AspNetCore.FunctionalTests +{ + public class CertificateLoaderTests : IDisposable + { + private readonly IConfiguration _configurationRoot = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Certificates:Certificate1:Source"] = "File", + ["Certificates:Certificate1:Path"] = "TestArtifacts/Certificate1.pfx", + ["Certificates:Certificate1:Password"] = "Password1", + ["Certificates:Certificate2:Source"] = "File", + ["Certificates:Certificate2:Path"] = "TestArtifacts/Certificate2.pfx", + ["Certificates:Certificate2:Password"] = "Password2", + ["TestConfig:CertificateName"] = "Certificate1", + ["TestConfig:CertificateNames"] = "Certificate1 Certificate2", + ["TestConfig:Certificate:Source"] = "File", + ["TestConfig:Certificate:Path"] = "TestArtifacts/Certificate3.pfx", + ["TestConfig:Certificate:Password"] = "Password3", + ["TestConfig:Certificates:Certificate4:Source"] = "File", + ["TestConfig:Certificates:Certificate4:Path"] = "TestArtifacts/Certificate4.pfx", + ["TestConfig:Certificates:Certificate4:Password"] = "Password4", + ["TestConfig:Certificates:Certificate5:Source"] = "File", + ["TestConfig:Certificates:Certificate5:Path"] = "TestArtifacts/Certificate5.pfx", + ["TestConfig:Certificates:Certificate5:Password"] = "Password5", + }) + .Build(); + + private readonly X509Certificate2 _certificate1 = new X509Certificate2("TestArtifacts/Certificate1.pfx", "Password1"); + private readonly X509Certificate2 _certificate2 = new X509Certificate2("TestArtifacts/Certificate2.pfx", "Password2"); + private readonly X509Certificate2 _certificate3 = new X509Certificate2("TestArtifacts/Certificate3.pfx", "Password3"); + private readonly X509Certificate2 _certificate4 = new X509Certificate2("TestArtifacts/Certificate4.pfx", "Password4"); + private readonly X509Certificate2 _certificate5 = new X509Certificate2("TestArtifacts/Certificate5.pfx", "Password5"); + + public void Dispose() + { + _certificate1.Dispose(); + _certificate2.Dispose(); + _certificate3.Dispose(); + _certificate4.Dispose(); + _certificate5.Dispose(); + } + + [Fact] + public void LoadsCertificateByName() + { + var certificate = new CertificateLoader(_configurationRoot.GetSection("Certificates")) + .Load("Certificate1"); + Assert.Equal(_certificate1, certificate); + } + + [Fact] + public void LoadsCertificateByNameFromConfigurationSection() + { + var certificate = new CertificateLoader(_configurationRoot.GetSection("Certificates")) + .Load(_configurationRoot.GetSection("TestConfig:CertificateName")) + .FirstOrDefault(); + Assert.Equal(_certificate1, certificate); + } + + [Fact] + public void LoadsCertificatesByNameFromConfigurationSection() + { + var certificates = new CertificateLoader(_configurationRoot.GetSection("Certificates")) + .Load(_configurationRoot.GetSection("TestConfig:CertificateNames")) + .ToArray(); + Assert.Equal(_certificate1, certificates[0]); + Assert.Equal(_certificate2, certificates[1]); + } + + [Fact] + public void LoadsCertificateInline() + { + var certificate = new CertificateLoader(_configurationRoot.GetSection("Certificates")) + .Load(_configurationRoot.GetSection("TestConfig:Certificate")) + .FirstOrDefault(); + Assert.Equal(_certificate3, certificate); + } + + [Fact] + public void LoadsMultipleCertificatesInline() + { + var certificates = new CertificateLoader(_configurationRoot.GetSection("Certificates")) + .Load(_configurationRoot.GetSection("TestConfig:Certificates")) + .ToArray(); + Assert.Equal(_certificate4, certificates[0]); + Assert.Equal(_certificate5, certificates[1]); + } + + [Fact] + public void LoadsCertificateInlineWithoutCertificateReferences() + { + var certificate = new CertificateLoader() + .Load(_configurationRoot.GetSection("TestConfig:Certificate")) + .FirstOrDefault(); + Assert.Equal(_certificate3, certificate); + } + + [Fact] + public void LoadsMultipleCertificatesInlineWithoutCertificateReferences() + { + var certificates = new CertificateLoader() + .Load(_configurationRoot.GetSection("TestConfig:Certificates")) + .ToArray(); + Assert.Equal(_certificate4, certificates[0]); + Assert.Equal(_certificate5, certificates[1]); + } + } +} diff --git a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj index a4676dc..8b4dee7 100644 --- a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj +++ b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj @@ -7,7 +7,12 @@ - + + + + + + diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx new file mode 100644 index 0000000000000000000000000000000000000000..c792c522488adf723e6f37b354a9f4178dedf98d GIT binary patch literal 2461 zcmV;O31apzf(e-d0Ru3C310>YDuzgg_YDCD0ic2jPy~VrOfZ58NHBr}{{{&vhDe6@ z4FLxRpn?PNFoFZ@0s#Opf&=9S2`Yw2hW8Bt2LUh~1_~;MNQUCLhc?KpZ79UX((b9gtz3l$ShHTW^w(4j=OB4)=~cBICF-(;4ox6qbLYN7}GC8?wNZs!fw~MG~CeO>3{q`?$!YE&|TW)7%ltY`g`J(4!EA+0qG<5f-@An+sS2s zyKPh^XzQo6k{=!X&J>f&qlC6rk8nC2#J1jlBp_18$8vKh-U$SOlzOIq)FBRBa&tg` zR?M@NvBD)UfX4@ypWaR=ez3Y(XT=|lyg$RizAl06K8 zr#J2k1>y5RV4K1`d870`A3QB{3^91xcGGJjnA z?o&nSF>%B)m;bj~^X${eq1{=%dD7bv41prq(x@sC@C{Ux|CklhNTJsPwbdHcnU6aO z_MGTcs3e!*5A|F5ucqU|`7#}Z*Khdo>V`x1Uo*CTvKwpEx#qsTud0(OiO61wr zWsY)LxoLde@Ttox)W3{NslcIT{a`vbL7X6WFHg+d7${@_O!$w4$&u67int_=`alI=5ZenfB@%ode@Q;fL^<;&t0j74IM#t*%O&Eb#E zO3Q0JLna^Ynq)c|cy-Ob09lVVleD}Lc6330Fpna<#uA`2$#oKNgzXM0hIDWxb(s$V z=mM&<1Vvca4{@Gq&+gV^)-i68+iSlE)4C9T6{pXBVY49f&hbPcQJn0EC!Fh`F_y+b z8JRS!vsd+UUsgd_z02z(#_2S|4Opb0xM2A#FoFd^1_>&LNQU<1f~E*r4=p<8OP?$r5q@pf%h z&EDD8e?4ydKLhh0X1jw|iGk*vgA=^gX0p1AZNR#zCAo>PbBd8-BIOL%BFE;!g*H$B z+%|ma9v2wNKO`Lk-c@XG95iEwCmv=hC0hfNaJ5Y?Vzl4P_ACkn6A6(*Xt)~gr24?& zc+?9X2d*291gesBlP(J`eTUNdobKwi*3?i~M`)ta3fK@;q{PEK+IrL7J&qIj20hwK z=FjLbo?5-}nuEo?0O*EM;WroV6@Y;9(0u=E0oZAnYKUmZgO@*BlUf&!yrg|^ZnYQS zWi0?G-9lB32QH2trNhMsRKwW8rzbJjF~4~>BJmD-z4*Ji`w!)TEIh(gI>dnMN`R$Z z=Jikm=|Vhkte489k2e;D)Mg|#af_PODc_GM$G;+R^1ik|-HCl-JgeBtM+o5lF|Di9xq1Z@Wssz@fb zbPnrus>$DBXV7xnJ^E#mF2Lg}$J%^TNrn2?I(t>5fuj^+cY|SyN8kR5EF23+oZTj1 z%$vAm>Ub#v)EGLri#9f_hQ_f2!3bDH)r&Jn${QBL?(7GS(_D`PTw67D>K*P2w4Q_&JRoj>7m zaA93;0I>mecVE^YkUui2b^mZQYw
=S3wwEn5^#$v~O)z)+%fl)_B{=vHjXvs{( zy@6^1ky#X!=Zw<(^`EROArA_6(b|7TC=s=GKFsVpF)PM*r(PTw&lG0&x0Io%!U9;2 zj=jihQxIh2UeSG6S6;zp+6D@@%7}M0&G{8oj}eIoFaW(cX}X(GB;Yqp2oNC;js&>K z4!MD=3=^sWTzc;ZOTmMxdB~e+wFY(hV*xIg)EC;wsZ`%SkjAUb@p2GYltVU5Ttp*T z(tN%{T<*Po);<(6l9pKk*YlK@3HVebh|1Js+1m~5UKrYT>M%KIi~nwzO{^ybk#t*r zNHqrglnI(D8rfWhnHTqMP8URj$C@;Qkk-JZ?!hcI;l{vNH9t+iV;tU}1irx}EWzGn zOf)IR=b-k6fsXnc*VTCl@&%|PujOc*)#Ii{f5SuZ-iR#-b2pc=YG2>2FDZ`JQ0=KS z;ZfPMXPehqQ>l~pxKFs91$jFd6km}OdnDGgAq4W!CMv_%kJO8i?vwn_V4kLz?&j|7 zi}in{dI)eyF*ULRbHg&bPFRx#AI%8;3jw~+y|_AIZ$(m2xa~0|Fe3&DDuzgg_YDCF z6)_eB6ng)VNS~j_hgq$}Xf4Qu|9}4v;V>~UAutIB1uG5%0vZJX1Qd*Ji=f=R`J3~d bWr6|Fzx*IOqG<#OPZEU6;7((40s;sCZpw)y literal 0 HcmV?d00001 diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate1.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate1.pfx new file mode 100644 index 0000000000000000000000000000000000000000..f3818332c60635d5fdbe8e32689fb2593af21a0a GIT binary patch literal 2461 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 YPo+QAPB8*=Nhos|&c>lW{cXvA0dz%@o&W#< literal 0 HcmV?d00001 diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate3.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate3.pfx new file mode 100644 index 0000000000000000000000000000000000000000..4c6f41138748cfbe16fee101419e50f27b5c2630 GIT binary patch literal 2461 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 Y2d~Db6ZhVO85%d=&Qn+ypnqHPUx7l2$N&HU literal 0 HcmV?d00001 diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate4.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate4.pfx new file mode 100644 index 0000000000000000000000000000000000000000..3e0a0d674ba9ee1def6a1351b99d6c5f87694da0 GIT binary patch literal 2461 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 Zn|OzB^{(zFh+AQU#Wa)|(fr$z{{kB4fTREb literal 0 HcmV?d00001 diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate5.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate5.pfx new file mode 100644 index 0000000000000000000000000000000000000000..b90f3ea2c1d1fa2c9337699dcb9611344f036b50 GIT binary patch literal 2461 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 bDtQQ-S#l!dm!AX(sDHr?UIdjn0s;sCQ_hN{ literal 0 HcmV?d00001 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 7118908c2d730670c16e9f8b2c532a262c951989..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2483 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 From dbf12c4b7d1745abaccf9f1ced2da275128ed3fb Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Fri, 28 Apr 2017 14:29:08 -0700 Subject: [PATCH 2/5] Change certificate loading order. --- src/Microsoft.AspNetCore/CertificateLoader.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.AspNetCore/CertificateLoader.cs b/src/Microsoft.AspNetCore/CertificateLoader.cs index 550cd45..d488e12 100644 --- a/src/Microsoft.AspNetCore/CertificateLoader.cs +++ b/src/Microsoft.AspNetCore/CertificateLoader.cs @@ -17,14 +17,14 @@ public class CertificateLoader private readonly IConfiguration _certificatesConfiguration; /// - /// Creates a new instance of . + /// Creates a new instance of . /// public CertificateLoader() { } /// - /// Creates a new instance of that can load certificates by name. + /// Creates a new instance of that can load certificates by name. /// /// An instance with information about certificates. public CertificateLoader(IConfiguration certificatesConfig) @@ -127,12 +127,12 @@ private class CertificateFileSource : CertificateSource 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) - #endif - ; + Exception error; + var certificate = +#if NETCOREAPP2_0 + TryLoad(X509KeyStorageFlags.EphemeralKeySet, out error) ?? +#endif + TryLoad(X509KeyStorageFlags.UserKeySet, out error); if (error != null) { From 1a7d085e733b3181ea04554188a53d1da7072402 Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Mon, 1 May 2017 17:56:52 -0700 Subject: [PATCH 3/5] Add tests. --- build/dependencies.props | 1 + samples/AppSettings/appsettings.json | 2 +- .../CertificateFileLoader.cs | 15 + src/Microsoft.AspNetCore/CertificateLoader.cs | 116 +- .../CertificateStoreLoader.cs | 56 + .../ICertificateFileLoader.cs | 12 + .../ICertificateStoreLoader.cs | 12 + .../Microsoft.AspNetCore.csproj | 2 +- .../Properties/AssemblyInfo.cs | 7 + .../CertificateLoaderTests.cs | 1023 +++++++++++++++-- ...icrosoft.AspNetCore.FunctionalTests.csproj | 1 + 11 files changed, 1111 insertions(+), 136 deletions(-) create mode 100644 src/Microsoft.AspNetCore/CertificateFileLoader.cs create mode 100644 src/Microsoft.AspNetCore/CertificateStoreLoader.cs create mode 100644 src/Microsoft.AspNetCore/ICertificateFileLoader.cs create mode 100644 src/Microsoft.AspNetCore/ICertificateStoreLoader.cs create mode 100644 src/Microsoft.AspNetCore/Properties/AssemblyInfo.cs 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 d488e12..536b883 100644 --- a/src/Microsoft.AspNetCore/CertificateLoader.cs +++ b/src/Microsoft.AspNetCore/CertificateLoader.cs @@ -15,21 +15,32 @@ namespace Microsoft.AspNetCore 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 certificates by name. + /// Creates a new instance of that can load certificate references from configuration. /// - /// An instance with information about certificates. - public CertificateLoader(IConfiguration certificatesConfig) + /// An with information about certificates. + public CertificateLoader(IConfiguration certificatesConfiguration) + : this(certificatesConfiguration, new CertificateFileLoader(), new CertificateStoreLoader()) { - _certificatesConfiguration = certificatesConfig; + _certificatesConfiguration = certificatesConfiguration; + } + + internal CertificateLoader(IConfiguration certificatesConfiguration, ICertificateFileLoader certificateFileLoader, ICertificateStoreLoader certificateStoreLoader) + { + _certificatesConfiguration = certificatesConfiguration; + _certificateFileLoader = certificateFileLoader; + _certificateStoreLoader = certificateStoreLoader; } /// @@ -47,16 +58,24 @@ public IEnumerable Load(IConfigurationSection certificateConfi if (certificateNames != null) { - foreach (var certificateName in certificateNames.Split(' ')) + foreach (var certificateName in certificateNames.Split(';')) { - certificates.Add(Load(certificateName)); + var certificate = LoadSingle(certificateName); + if (certificate != null) + { + certificates.Add(certificate); + } } } else { if (certificateConfiguration["Source"] != null) { - certificates.Add(LoadSingle(certificateConfiguration)); + var certificate = LoadSingle(certificateConfiguration); + if (certificate != null) + { + certificates.Add(certificate); + } } else { @@ -75,11 +94,11 @@ public IEnumerable Load(IConfigurationSection certificateConfi /// This method only works if the instance was constructed with /// a reference to an instance containing named certificates. /// - public X509Certificate2 Load(string certificateName) + private X509Certificate2 LoadSingle(string certificateName) { var certificateConfiguration = _certificatesConfiguration?.GetSection(certificateName); - if (certificateConfiguration == null) + if (!certificateConfiguration.Exists()) { throw new InvalidOperationException($"No certificate named {certificateName} found in configuration"); } @@ -95,10 +114,10 @@ private X509Certificate2 LoadSingle(IConfigurationSection certificateConfigurati switch (sourceKind.ToLowerInvariant()) { case "file": - certificateSource = new CertificateFileSource(); + certificateSource = new CertificateFileSource(_certificateFileLoader); break; case "store": - certificateSource = new CertificateStoreSource(); + certificateSource = new CertificateStoreSource(_certificateStoreLoader); break; default: throw new InvalidOperationException($"Invalid certificate source kind: {sourceKind}"); @@ -110,7 +129,9 @@ private X509Certificate2 LoadSingle(IConfigurationSection certificateConfigurati } private IEnumerable LoadMultiple(IConfigurationSection certificatesConfiguration) - => certificatesConfiguration.GetChildren().Select(LoadSingle); + => certificatesConfiguration.GetChildren() + .Select(LoadSingle) + .Where(c => c != null); private abstract class CertificateSource { @@ -121,18 +142,25 @@ private abstract class CertificateSource 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() { - Exception error; - var certificate = + var certificate = TryLoad(X509KeyStorageFlags.DefaultKeySet, out var error) + ?? TryLoad(X509KeyStorageFlags.UserKeySet, out error) #if NETCOREAPP2_0 - TryLoad(X509KeyStorageFlags.EphemeralKeySet, out error) ?? + ?? TryLoad(X509KeyStorageFlags.EphemeralKeySet, out error) #endif - TryLoad(X509KeyStorageFlags.UserKeySet, out error); + ; if (error != null) { @@ -146,7 +174,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; } @@ -160,6 +188,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; } @@ -172,52 +207,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/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 index fad12e4..7cf3787 100644 --- a/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs +++ b/test/Microsoft.AspNetCore.FunctionalTests/CertificateLoaderTests.cs @@ -7,112 +7,993 @@ using System.Security.Cryptography.X509Certificates; using Microsoft.Extensions.Configuration; using Xunit; +using Moq; namespace Microsoft.AspNetCore.FunctionalTests { - public class CertificateLoaderTests : IDisposable + public class CertificateLoaderTests { - private readonly IConfiguration _configurationRoot = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - ["Certificates:Certificate1:Source"] = "File", - ["Certificates:Certificate1:Path"] = "TestArtifacts/Certificate1.pfx", - ["Certificates:Certificate1:Password"] = "Password1", - ["Certificates:Certificate2:Source"] = "File", - ["Certificates:Certificate2:Path"] = "TestArtifacts/Certificate2.pfx", - ["Certificates:Certificate2:Password"] = "Password2", - ["TestConfig:CertificateName"] = "Certificate1", - ["TestConfig:CertificateNames"] = "Certificate1 Certificate2", - ["TestConfig:Certificate:Source"] = "File", - ["TestConfig:Certificate:Path"] = "TestArtifacts/Certificate3.pfx", - ["TestConfig:Certificate:Password"] = "Password3", - ["TestConfig:Certificates:Certificate4:Source"] = "File", - ["TestConfig:Certificates:Certificate4:Path"] = "TestArtifacts/Certificate4.pfx", - ["TestConfig:Certificates:Certificate4:Password"] = "Password4", - ["TestConfig:Certificates:Certificate5:Source"] = "File", - ["TestConfig:Certificates:Certificate5:Path"] = "TestArtifacts/Certificate5.pfx", - ["TestConfig:Certificates:Certificate5:Password"] = "Password5", - }) - .Build(); + [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(); - private readonly X509Certificate2 _certificate1 = new X509Certificate2("TestArtifacts/Certificate1.pfx", "Password1"); - private readonly X509Certificate2 _certificate2 = new X509Certificate2("TestArtifacts/Certificate2.pfx", "Password2"); - private readonly X509Certificate2 _certificate3 = new X509Certificate2("TestArtifacts/Certificate3.pfx", "Password3"); - private readonly X509Certificate2 _certificate4 = new X509Certificate2("TestArtifacts/Certificate4.pfx", "Password4"); - private readonly X509Certificate2 _certificate5 = new X509Certificate2("TestArtifacts/Certificate5.pfx", "Password5"); + 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(); + } - public void Dispose() + [Fact] + public void Throws_SingleCertificateName_File_KeyNotFound() { - _certificate1.Dispose(); - _certificate2.Dispose(); - _certificate3.Dispose(); - _certificate4.Dispose(); - _certificate5.Dispose(); + 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 LoadsCertificateByName() + public void Throws_SingleCertificateName_File_FileNotFound() { - var certificate = new CertificateLoader(_configurationRoot.GetSection("Certificates")) - .Load("Certificate1"); - Assert.Equal(_certificate1, certificate); + 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 LoadsCertificateByNameFromConfigurationSection() + public void Loads_SingleCertificateName_Store() { - var certificate = new CertificateLoader(_configurationRoot.GetSection("Certificates")) - .Load(_configurationRoot.GetSection("TestConfig:CertificateName")) - .FirstOrDefault(); - Assert.Equal(_certificate1, certificate); + 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 LoadsCertificatesByNameFromConfigurationSection() + public void Throws_SingleCertificateName_Store_KeyNotFound() { - var certificates = new CertificateLoader(_configurationRoot.GetSection("Certificates")) - .Load(_configurationRoot.GetSection("TestConfig:CertificateNames")) - .ToArray(); - Assert.Equal(_certificate1, certificates[0]); - Assert.Equal(_certificate2, certificates[1]); + 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 LoadsCertificateInline() + public void ReturnsNull_SingleCertificateName_Store_NotFoundInStore() { - var certificate = new CertificateLoader(_configurationRoot.GetSection("Certificates")) - .Load(_configurationRoot.GetSection("TestConfig:Certificate")) - .FirstOrDefault(); - Assert.Equal(_certificate3, certificate); + 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 LoadsMultipleCertificatesInline() + public void Loads_MultipleCertificateNames_File() { - var certificates = new CertificateLoader(_configurationRoot.GetSection("Certificates")) - .Load(_configurationRoot.GetSection("TestConfig:Certificates")) - .ToArray(); - Assert.Equal(_certificate4, certificates[0]); - Assert.Equal(_certificate5, certificates[1]); + 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 LoadsCertificateInlineWithoutCertificateReferences() + public void Loads_MultipleCertificateNames_Store() { - var certificate = new CertificateLoader() - .Load(_configurationRoot.GetSection("TestConfig:Certificate")) - .FirstOrDefault(); - Assert.Equal(_certificate3, certificate); + 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 LoadsMultipleCertificatesInlineWithoutCertificateReferences() + 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 certificates = new CertificateLoader() - .Load(_configurationRoot.GetSection("TestConfig:Certificates")) - .ToArray(); - Assert.Equal(_certificate4, certificates[0]); - Assert.Equal(_certificate5, certificates[1]); + 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 8b4dee7..eca67f6 100644 --- a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj +++ b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj @@ -22,6 +22,7 @@ + From 389682094e6838f899679096db0f1f7260891a0f Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Mon, 1 May 2017 18:00:55 -0700 Subject: [PATCH 4/5] Remove excess test certificates. --- .../Microsoft.AspNetCore.FunctionalTests.csproj | 5 ----- .../TestArtifacts/Certificate1.pfx | Bin 2461 -> 0 bytes .../TestArtifacts/Certificate2.pfx | Bin 2461 -> 0 bytes .../TestArtifacts/Certificate3.pfx | Bin 2461 -> 0 bytes .../TestArtifacts/Certificate4.pfx | Bin 2461 -> 0 bytes .../TestArtifacts/Certificate5.pfx | Bin 2461 -> 0 bytes 6 files changed, 5 deletions(-) delete mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate1.pfx delete mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate2.pfx delete mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate3.pfx delete mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate4.pfx delete mode 100644 test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate5.pfx diff --git a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj index eca67f6..13d57e6 100644 --- a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj +++ b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj @@ -8,11 +8,6 @@ - - - - - diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate1.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate1.pfx deleted file mode 100644 index f3818332c60635d5fdbe8e32689fb2593af21a0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2461 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 YPo+QAPB8*=Nhos|&c>lW{cXvA0dz%@o&W#< diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate3.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate3.pfx deleted file mode 100644 index 4c6f41138748cfbe16fee101419e50f27b5c2630..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2461 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 Y2d~Db6ZhVO85%d=&Qn+ypnqHPUx7l2$N&HU diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate4.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate4.pfx deleted file mode 100644 index 3e0a0d674ba9ee1def6a1351b99d6c5f87694da0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2461 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 Zn|OzB^{(zFh+AQU#Wa)|(fr$z{{kB4fTREb diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate5.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate5.pfx deleted file mode 100644 index b90f3ea2c1d1fa2c9337699dcb9611344f036b50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2461 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 bDtQQ-S#l!dm!AX(sDHr?UIdjn0s;sCQ_hN{ From 357df31afb2da6de31e97b8f1e4fa842bed43d9a Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Mon, 1 May 2017 18:04:17 -0700 Subject: [PATCH 5/5] var --- src/Microsoft.AspNetCore/CertificateLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore/CertificateLoader.cs b/src/Microsoft.AspNetCore/CertificateLoader.cs index 536b883..be006b4 100644 --- a/src/Microsoft.AspNetCore/CertificateLoader.cs +++ b/src/Microsoft.AspNetCore/CertificateLoader.cs @@ -53,8 +53,7 @@ internal CertificateLoader(IConfiguration certificatesConfiguration, ICertificat public IEnumerable Load(IConfigurationSection certificateConfiguration) { var certificateNames = certificateConfiguration.Value; - - List certificates = new List(); + var certificates = new List(); if (certificateNames != null) {