diff --git a/build/dependencies.props b/build/dependencies.props
index 1c6eb173a4..1ab5074b56 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -26,14 +26,12 @@
2.1.1
- 2.1.1
2.1.0
2.1.1
2.1.2
2.1.2
2.1.2
2.1.2
- 2.1.1
2.1.1
2.1.1
2.1.1
@@ -47,7 +45,6 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.1
2.1.1
2.1.1
@@ -73,22 +70,14 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.6
2.1.1
2.1.1
2.1.1
2.1.6
- 2.1.1
- 2.1.1
- 2.1.1
- 2.1.1
- 2.1.1
- 2.1.1
2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.1
diff --git a/build/external-dependencies.props b/build/external-dependencies.props
index 7cb96166b2..f3bf216028 100644
--- a/build/external-dependencies.props
+++ b/build/external-dependencies.props
@@ -15,14 +15,12 @@
-
-
@@ -36,7 +34,6 @@
-
@@ -62,21 +59,13 @@
-
-
-
-
-
-
-
-
diff --git a/build/repo.props b/build/repo.props
index 881ac8ee77..eb668c963c 100644
--- a/build/repo.props
+++ b/build/repo.props
@@ -75,6 +75,7 @@
$(RepositoryRoot)src\Html\**\*.*proj;
$(RepositoryRoot)src\Servers\**\*.csproj;
$(RepositoryRoot)src\Servers\**\*.pkgproj;
+ $(RepositoryRoot)src\Shared\**\*.*proj;
$(RepositoryRoot)src\Tools\**\*.*proj;
$(RepositoryRoot)src\Middleware\**\*.*proj;
"
diff --git a/eng/Dependencies.props b/eng/Dependencies.props
index 7b1eba3c54..39c05aae06 100644
--- a/eng/Dependencies.props
+++ b/eng/Dependencies.props
@@ -25,7 +25,6 @@ and are generated based on the last package release.
-
@@ -34,14 +33,12 @@ and are generated based on the last package release.
-
-
@@ -56,12 +53,7 @@ and are generated based on the last package release.
-
-
-
-
-
@@ -71,6 +63,7 @@ and are generated based on the last package release.
+
@@ -79,6 +72,7 @@ and are generated based on the last package release.
+
diff --git a/src/AuthSamples/build/dependencies.props b/src/AuthSamples/build/dependencies.props
index 01ccc8687a..c8622f6dd3 100644
--- a/src/AuthSamples/build/dependencies.props
+++ b/src/AuthSamples/build/dependencies.props
@@ -57,8 +57,6 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
- 2.1.1
3.14.2
5.2.0
2.0.0
diff --git a/src/AzureIntegration/Directory.Build.props b/src/AzureIntegration/Directory.Build.props
index 24c8148124..5785b0e457 100644
--- a/src/AzureIntegration/Directory.Build.props
+++ b/src/AzureIntegration/Directory.Build.props
@@ -16,6 +16,7 @@
true
true
true
+ $(MSBuildThisFileDirectory)..\Shared\
diff --git a/src/AzureIntegration/build/dependencies.props b/src/AzureIntegration/build/dependencies.props
index f338fce0d4..640d5a8d6e 100644
--- a/src/AzureIntegration/build/dependencies.props
+++ b/src/AzureIntegration/build/dependencies.props
@@ -51,6 +51,5 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
-
\ No newline at end of file
+
diff --git a/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj b/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
index 6bf10bc884..dd2aa41bb2 100644
--- a/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
+++ b/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
@@ -9,13 +9,13 @@
+
-
diff --git a/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj b/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj
index 021b3fde2c..e1c44678f6 100644
--- a/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj
+++ b/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj
@@ -12,10 +12,7 @@ Microsoft.AspNetCore.DataProtection.IDataProtector
-
-
-
-
+
diff --git a/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj b/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj
index 3708e92927..f6f11fd3b3 100644
--- a/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj
+++ b/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj
@@ -8,10 +8,13 @@
aspnetcore;json;jsonpatch
+
+
+
+
-
diff --git a/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj b/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj
index 627b36bbc2..fbefc75eef 100644
--- a/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj
+++ b/src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj
@@ -8,6 +8,11 @@
aspnetcore;hosting
+
+
+
+
+
@@ -20,8 +25,6 @@
-
-
diff --git a/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj b/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
index e693a22278..d04c320b59 100644
--- a/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
+++ b/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
@@ -16,6 +16,7 @@
+
@@ -25,7 +26,6 @@
-
diff --git a/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj b/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
index 4344d0ae8e..e9d04e1ebc 100644
--- a/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
+++ b/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
@@ -9,10 +9,13 @@
aspnetcore
+
+
+
+
-
diff --git a/src/Http/Routing.Abstractions/src/Microsoft.AspNetCore.Routing.Abstractions.csproj b/src/Http/Routing.Abstractions/src/Microsoft.AspNetCore.Routing.Abstractions.csproj
index edb292bb22..ff154fe535 100644
--- a/src/Http/Routing.Abstractions/src/Microsoft.AspNetCore.Routing.Abstractions.csproj
+++ b/src/Http/Routing.Abstractions/src/Microsoft.AspNetCore.Routing.Abstractions.csproj
@@ -11,8 +11,11 @@ Microsoft.AspNetCore.Routing.RouteData
aspnetcore;routing
+
+
+
+
-
diff --git a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
index cc8684caf4..af4bc3a24f 100644
--- a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
+++ b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
@@ -11,6 +11,10 @@ Microsoft.AspNetCore.Routing.RouteCollection
aspnetcore;routing
+
+
+
+
@@ -18,6 +22,5 @@ Microsoft.AspNetCore.Routing.RouteCollection
-
diff --git a/src/Http/WebUtilities/src/Microsoft.AspNetCore.WebUtilities.csproj b/src/Http/WebUtilities/src/Microsoft.AspNetCore.WebUtilities.csproj
index 3c7d2d8255..fdc9592cc8 100644
--- a/src/Http/WebUtilities/src/Microsoft.AspNetCore.WebUtilities.csproj
+++ b/src/Http/WebUtilities/src/Microsoft.AspNetCore.WebUtilities.csproj
@@ -10,7 +10,10 @@
-
+
+
+
+
diff --git a/src/Identity/Directory.Build.props b/src/Identity/Directory.Build.props
index 230ecfc69e..f7f18cf416 100644
--- a/src/Identity/Directory.Build.props
+++ b/src/Identity/Directory.Build.props
@@ -15,6 +15,7 @@
$(MSBuildThisFileDirectory)build\Key.snk
true
true
+ $(MSBuildThisFileDirectory)..\Shared\
diff --git a/src/Identity/build/dependencies.props b/src/Identity/build/dependencies.props
index 21e3cb41e5..3ae698e9d8 100644
--- a/src/Identity/build/dependencies.props
+++ b/src/Identity/build/dependencies.props
@@ -73,8 +73,6 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
- 2.1.1
2.1.1
2.1.1
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj
index f418a50148..23e9b782f1 100644
--- a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj
@@ -10,12 +10,12 @@
+
-
diff --git a/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj b/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj
index a5ae405dcd..9bdc1cea77 100644
--- a/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj
+++ b/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj
@@ -10,6 +10,8 @@
+
+
@@ -20,8 +22,6 @@
-
-
diff --git a/src/Mvc/Directory.Build.props b/src/Mvc/Directory.Build.props
index fad2bb0610..db07d3358b 100644
--- a/src/Mvc/Directory.Build.props
+++ b/src/Mvc/Directory.Build.props
@@ -16,6 +16,7 @@
true
true
true
+ $(MSBuildThisFileDirectory)..\Shared\
diff --git a/src/Mvc/build/dependencies.props b/src/Mvc/build/dependencies.props
index 3e21512352..3b46192a78 100644
--- a/src/Mvc/build/dependencies.props
+++ b/src/Mvc/build/dependencies.props
@@ -71,10 +71,8 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.1
2.1.0
2.1.1
@@ -88,13 +86,9 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.1
2.1.1
2.1.1
- 2.1.1
- 2.1.1
- 2.1.1
2.1.1
2.1.1
2.1.1
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Abstractions/Microsoft.AspNetCore.Mvc.Abstractions.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Abstractions/Microsoft.AspNetCore.Mvc.Abstractions.csproj
index 4ebbaeec61..41cfcba32b 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Abstractions/Microsoft.AspNetCore.Mvc.Abstractions.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Abstractions/Microsoft.AspNetCore.Mvc.Abstractions.csproj
@@ -10,12 +10,15 @@ Microsoft.AspNetCore.Mvc.IActionResult
aspnetcore;aspnetcoremvc
+
+
+
+
+
+
-
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj
index 9ea5be90f4..0e6afbe939 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.ApiExplorer/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj
@@ -8,12 +8,15 @@
aspnetcore;aspnetcoremvc
+
+
+
+
+
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Microsoft.AspNetCore.Mvc.Core.csproj
index b63ba839ef..a54324689b 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Microsoft.AspNetCore.Mvc.Core.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/Microsoft.AspNetCore.Mvc.Core.csproj
@@ -17,7 +17,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute
-
+
+
+
+
+
+
@@ -30,17 +35,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute
-
-
-
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Microsoft.AspNetCore.Mvc.DataAnnotations.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Microsoft.AspNetCore.Mvc.DataAnnotations.csproj
index 336474aae0..542474780e 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Microsoft.AspNetCore.Mvc.DataAnnotations.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Microsoft.AspNetCore.Mvc.DataAnnotations.csproj
@@ -8,11 +8,14 @@
aspnetcore;aspnetcoremvc
+
+
+
+
+
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Json/Microsoft.AspNetCore.Mvc.Formatters.Json.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Json/Microsoft.AspNetCore.Mvc.Formatters.Json.csproj
index 608e370257..c57c948d9e 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Json/Microsoft.AspNetCore.Mvc.Formatters.Json.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Json/Microsoft.AspNetCore.Mvc.Formatters.Json.csproj
@@ -8,10 +8,13 @@
aspnetcore;aspnetcoremvc;json
+
+
+
+
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj
index b716f5cbbb..ebcf792899 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/Microsoft.AspNetCore.Mvc.Formatters.Xml.csproj
@@ -9,9 +9,11 @@
-
+
+
+
-
-
+
+
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Localization/Microsoft.AspNetCore.Mvc.Localization.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Localization/Microsoft.AspNetCore.Mvc.Localization.csproj
index ea50d25312..c8d53a4da1 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Localization/Microsoft.AspNetCore.Mvc.Localization.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Localization/Microsoft.AspNetCore.Mvc.Localization.csproj
@@ -11,13 +11,16 @@ Microsoft.AspNetCore.Mvc.Localization.IViewLocalizer
aspnetcore;aspnetcoremvc;localization
+
+
+
+
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj
index 74e943a8d9..2679f72275 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Razor/Microsoft.AspNetCore.Mvc.Razor.csproj
@@ -10,6 +10,12 @@
true
+
+
+
+
+
+
@@ -19,10 +25,7 @@
-
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.RazorPages/Microsoft.AspNetCore.Mvc.RazorPages.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.RazorPages/Microsoft.AspNetCore.Mvc.RazorPages.csproj
index f0be433ea5..fd59e58282 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.RazorPages/Microsoft.AspNetCore.Mvc.RazorPages.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.RazorPages/Microsoft.AspNetCore.Mvc.RazorPages.csproj
@@ -8,14 +8,17 @@
aspnetcore;aspnetcoremvc;cshtml;razor
+
+
+
+
+
+
+
-
-
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.TagHelpers/Microsoft.AspNetCore.Mvc.TagHelpers.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.TagHelpers/Microsoft.AspNetCore.Mvc.TagHelpers.csproj
index 75d40d73c2..c77fa2c2b4 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.TagHelpers/Microsoft.AspNetCore.Mvc.TagHelpers.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.TagHelpers/Microsoft.AspNetCore.Mvc.TagHelpers.csproj
@@ -10,6 +10,7 @@
+
@@ -21,6 +22,5 @@
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj
index 27d839320d..54a2f8e042 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj
@@ -13,6 +13,14 @@ Microsoft.AspNetCore.Mvc.ViewComponent
aspnetcore;aspnetcoremvc
+
+
+
+
+
+
+
+
@@ -22,12 +30,7 @@ Microsoft.AspNetCore.Mvc.ViewComponent
-
-
-
-
-
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj b/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj
index ab1d11a599..a40f7a0d15 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj
@@ -10,13 +10,16 @@ System.Web.Http.ApiController
aspnetcore;aspnetcoremvc;aspnetwebapi
+
+
+
+
-
diff --git a/src/Razor/build/dependencies.props b/src/Razor/build/dependencies.props
index e128924edb..294cf2f14e 100644
--- a/src/Razor/build/dependencies.props
+++ b/src/Razor/build/dependencies.props
@@ -65,7 +65,6 @@
2.1.1
2.1.0
2.1.1
- 2.1.1
2.1.1
2.1.1
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj b/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
index b58a7f9c3f..e3102cbf8f 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Runtime/Microsoft.AspNetCore.Razor.Runtime.csproj
@@ -12,7 +12,6 @@
-
diff --git a/src/Security/build/dependencies.props b/src/Security/build/dependencies.props
index 828f9c7ab2..fb898d743e 100644
--- a/src/Security/build/dependencies.props
+++ b/src/Security/build/dependencies.props
@@ -52,7 +52,6 @@
2.1.1
2.1.1
2.1.1
- 2.1.1
2.1.1
-
\ No newline at end of file
+
diff --git a/src/Security/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj b/src/Security/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj
index 7e3ce4eb39..ce5b4c6f93 100644
--- a/src/Security/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj
+++ b/src/Security/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj
@@ -8,6 +8,10 @@
aspnetcore;authentication;security
+
+
+
+
@@ -15,7 +19,6 @@
-
diff --git a/src/Security/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj b/src/Security/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj
index 16e4aa2622..9516b08388 100644
--- a/src/Security/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj
+++ b/src/Security/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj
@@ -8,13 +8,16 @@
aspnetcore;authorization
+
+
+
+
-
diff --git a/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj b/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj
index dae8056002..55d5278ff9 100644
--- a/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj
+++ b/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj
@@ -11,10 +11,10 @@
+
-
diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs
new file mode 100644
index 0000000000..4e2a0a9964
--- /dev/null
+++ b/src/Shared/CertificateGeneration/CertificateManager.cs
@@ -0,0 +1,720 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.AspNetCore.Certificates.Generation
+{
+ internal class CertificateManager
+ {
+ public const string AspNetHttpsOid = "1.3.6.1.4.1.311.84.1.1";
+ public const string AspNetHttpsOidFriendlyName = "ASP.NET Core HTTPS development certificate";
+
+ public const string AspNetIdentityOid = "1.3.6.1.4.1.311.84.1.2";
+ public const string AspNetIdentityOidFriendlyName = "ASP.NET Core Identity Json Web Token signing development certificate";
+
+ private const string ServerAuthenticationEnhancedKeyUsageOid = "1.3.6.1.5.5.7.3.1";
+ private const string ServerAuthenticationEnhancedKeyUsageOidFriendlyName = "Server Authentication";
+
+ private const string LocalhostHttpsDnsName = "localhost";
+ private const string LocalhostHttpsDistinguishedName = "CN=" + LocalhostHttpsDnsName;
+
+ private const string IdentityDistinguishedName = "CN=Microsoft.AspNetCore.Identity.Signing";
+
+ public const int RSAMinimumKeySizeInBits = 2048;
+
+ private static readonly TimeSpan MaxRegexTimeout = TimeSpan.FromMinutes(1);
+ private const string CertificateSubjectRegex = "CN=(.*[^,]+).*";
+ private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain";
+ private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db";
+ private const string MacOSFindCertificateCommandLine = "security";
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+ private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain;
+#endif
+ private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)";
+ private const string MacOSRemoveCertificateTrustCommandLine = "sudo";
+ private const string MacOSRemoveCertificateTrustCommandLineArgumentsFormat = "security remove-trusted-cert -d {0}";
+ private const string MacOSDeleteCertificateCommandLine = "sudo";
+ private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}";
+ private const string MacOSTrustCertificateCommandLine = "sudo";
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+ private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " ";
+#endif
+ private const int UserCancelledErrorCode = 1223;
+
+ public IList ListCertificates(
+ CertificatePurpose purpose,
+ StoreName storeName,
+ StoreLocation location,
+ bool isValid,
+ bool requireExportable = true)
+ {
+ var certificates = new List();
+ try
+ {
+ using (var store = new X509Store(storeName, location))
+ {
+ store.Open(OpenFlags.ReadOnly);
+ certificates.AddRange(store.Certificates.OfType());
+ IEnumerable matchingCertificates = certificates;
+ switch (purpose)
+ {
+ case CertificatePurpose.All:
+ matchingCertificates = matchingCertificates
+ .Where(c => HasOid(c, AspNetHttpsOid) || HasOid(c, AspNetIdentityOid));
+ break;
+ case CertificatePurpose.HTTPS:
+ matchingCertificates = matchingCertificates
+ .Where(c => HasOid(c, AspNetHttpsOid));
+ break;
+ case CertificatePurpose.Signing:
+ matchingCertificates = matchingCertificates
+ .Where(c => HasOid(c, AspNetIdentityOid));
+ break;
+ default:
+ break;
+ }
+ if (isValid)
+ {
+ // Ensure the certificate hasn't expired, has a private key and its exportable
+ // (for container/unix scenarios).
+ var now = DateTimeOffset.Now;
+ matchingCertificates = matchingCertificates
+ .Where(c => c.NotBefore <= now &&
+ now <= c.NotAfter &&
+ (!requireExportable || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || IsExportable(c)));
+ }
+
+ // We need to enumerate the certificates early to prevent dispoisng issues.
+ matchingCertificates = matchingCertificates.ToList();
+
+ var certificatesToDispose = certificates.Except(matchingCertificates);
+ DisposeCertificates(certificatesToDispose);
+
+ store.Close();
+
+ return (IList)matchingCertificates;
+ }
+ }
+ catch
+ {
+ DisposeCertificates(certificates);
+ certificates.Clear();
+ return certificates;
+ }
+
+ bool HasOid(X509Certificate2 certificate, string oid) =>
+ certificate.Extensions.OfType()
+ .Any(e => string.Equals(oid, e.Oid.Value, StringComparison.Ordinal));
+#if !XPLAT
+ bool IsExportable(X509Certificate2 c) =>
+ ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
+ rsaPrivateKey.CspKeyContainerInfo.Exportable) ||
+ (c.GetRSAPrivateKey() is RSACng cngPrivateKey &&
+ cngPrivateKey.Key.ExportPolicy == CngExportPolicies.AllowExport));
+#else
+ // Only check for RSA CryptoServiceProvider and do not fail in XPlat tooling as
+ // System.Security.Cryptography.Cng is not pat of the shared framework and we don't
+ // want to bring the dependency in on CLI scenarios. This functionality will be used
+ // on CLI scenarios as part of the first run experience, so checking the exportability
+ // of the certificate is not important.
+ bool IsExportable(X509Certificate2 c) =>
+ ((c.GetRSAPrivateKey() is RSACryptoServiceProvider rsaPrivateKey &&
+ rsaPrivateKey.CspKeyContainerInfo.Exportable) || !(c.GetRSAPrivateKey() is RSACryptoServiceProvider));
+#endif
+ }
+
+ private void DisposeCertificates(IEnumerable disposables)
+ {
+ foreach (var disposable in disposables)
+ {
+ try
+ {
+ disposable.Dispose();
+ }
+ catch
+ {
+ }
+ }
+ }
+
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+
+ public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride)
+ {
+ var subject = new X500DistinguishedName(subjectOverride ?? LocalhostHttpsDistinguishedName);
+ var extensions = new List();
+ var sanBuilder = new SubjectAlternativeNameBuilder();
+ sanBuilder.AddDnsName(LocalhostHttpsDnsName);
+
+ var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true);
+ var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
+ new OidCollection() {
+ new Oid(
+ ServerAuthenticationEnhancedKeyUsageOid,
+ ServerAuthenticationEnhancedKeyUsageOidFriendlyName)
+ },
+ critical: true);
+
+ var basicConstraints = new X509BasicConstraintsExtension(
+ certificateAuthority: false,
+ hasPathLengthConstraint: false,
+ pathLengthConstraint: 0,
+ critical: true);
+
+ var aspNetHttpsExtension = new X509Extension(
+ new AsnEncodedData(
+ new Oid(AspNetHttpsOid, AspNetHttpsOidFriendlyName),
+ Encoding.ASCII.GetBytes(AspNetHttpsOidFriendlyName)),
+ critical: false);
+
+ extensions.Add(basicConstraints);
+ extensions.Add(keyUsage);
+ extensions.Add(enhancedKeyUsage);
+ extensions.Add(sanBuilder.Build(critical: true));
+ extensions.Add(aspNetHttpsExtension);
+
+ var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter);
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ certificate.FriendlyName = AspNetHttpsOidFriendlyName;
+ }
+
+ return certificate;
+ }
+
+ public X509Certificate2 CreateApplicationTokenSigningDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride)
+ {
+ var subject = new X500DistinguishedName(subjectOverride ?? IdentityDistinguishedName);
+ var extensions = new List();
+
+ var keyUsage = new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true);
+ var enhancedKeyUsage = new X509EnhancedKeyUsageExtension(
+ new OidCollection() {
+ new Oid(
+ ServerAuthenticationEnhancedKeyUsageOid,
+ ServerAuthenticationEnhancedKeyUsageOidFriendlyName)
+ },
+ critical: true);
+
+ var basicConstraints = new X509BasicConstraintsExtension(
+ certificateAuthority: false,
+ hasPathLengthConstraint: false,
+ pathLengthConstraint: 0,
+ critical: true);
+
+ var aspNetIdentityExtension = new X509Extension(
+ new AsnEncodedData(
+ new Oid(AspNetIdentityOid, AspNetIdentityOidFriendlyName),
+ Encoding.ASCII.GetBytes(AspNetIdentityOidFriendlyName)),
+ critical: false);
+
+ extensions.Add(basicConstraints);
+ extensions.Add(keyUsage);
+ extensions.Add(enhancedKeyUsage);
+ extensions.Add(aspNetIdentityExtension);
+
+ var certificate = CreateSelfSignedCertificate(subject, extensions, notBefore, notAfter);
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ certificate.FriendlyName = AspNetIdentityOidFriendlyName;
+ }
+
+ return certificate;
+ }
+
+ public X509Certificate2 CreateSelfSignedCertificate(
+ X500DistinguishedName subject,
+ IEnumerable extensions,
+ DateTimeOffset notBefore,
+ DateTimeOffset notAfter)
+ {
+ var key = CreateKeyMaterial(RSAMinimumKeySizeInBits);
+
+ var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+ foreach (var extension in extensions)
+ {
+ request.CertificateExtensions.Add(extension);
+ }
+
+ return request.CreateSelfSigned(notBefore, notAfter);
+
+ RSA CreateKeyMaterial(int minimumKeySize)
+ {
+ var rsa = RSA.Create(minimumKeySize);
+ if (rsa.KeySize < minimumKeySize)
+ {
+ throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits");
+ }
+
+ return rsa;
+ }
+ }
+
+ public X509Certificate2 SaveCertificateInStore(X509Certificate2 certificate, StoreName name, StoreLocation location)
+ {
+ var imported = certificate;
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ // On non OSX systems we need to export the certificate and import it so that the transient
+ // key that we generated gets persisted.
+ var export = certificate.Export(X509ContentType.Pkcs12, "");
+ imported = new X509Certificate2(export, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
+ Array.Clear(export, 0, export.Length);
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ imported.FriendlyName = certificate.FriendlyName;
+ }
+
+ using (var store = new X509Store(name, location))
+ {
+ store.Open(OpenFlags.ReadWrite);
+ store.Add(imported);
+ store.Close();
+ };
+
+ return imported;
+ }
+
+ public void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password)
+ {
+ if (Path.GetDirectoryName(path) != "")
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ }
+
+ if (includePrivateKey)
+ {
+ var bytes = certificate.Export(X509ContentType.Pkcs12, password);
+ try
+ {
+ File.WriteAllBytes(path, bytes);
+ }
+ finally
+ {
+ Array.Clear(bytes, 0, bytes.Length);
+ }
+ }
+ else
+ {
+ var bytes = certificate.Export(X509ContentType.Cert);
+ File.WriteAllBytes(path, bytes);
+ }
+ }
+
+ public void TrustCertificate(X509Certificate2 certificate)
+ {
+ // Strip certificate of the private key if any.
+ var publicCertificate = new X509Certificate2(certificate.Export(X509ContentType.Cert));
+
+ if (!IsTrusted(publicCertificate))
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ TrustCertificateOnWindows(certificate, publicCertificate);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ TrustCertificateOnMac(publicCertificate);
+ }
+ }
+ }
+
+ private void TrustCertificateOnMac(X509Certificate2 publicCertificate)
+ {
+ var tmpFile = Path.GetTempFileName();
+ try
+ {
+ ExportCertificate(publicCertificate, tmpFile, includePrivateKey: false, password: null);
+ using (var process = Process.Start(MacOSTrustCertificateCommandLine, MacOSTrustCertificateCommandLineArguments + tmpFile))
+ {
+ process.WaitForExit();
+ if (process.ExitCode != 0)
+ {
+ throw new InvalidOperationException("There was an error trusting the certificate.");
+ }
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (File.Exists(tmpFile))
+ {
+ File.Delete(tmpFile);
+ }
+ }
+ catch
+ {
+ // We don't care if we can't delete the temp file.
+ }
+ }
+ }
+
+ private static void TrustCertificateOnWindows(X509Certificate2 certificate, X509Certificate2 publicCertificate)
+ {
+ publicCertificate.FriendlyName = certificate.FriendlyName;
+
+ using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
+ {
+ store.Open(OpenFlags.ReadWrite);
+ try
+ {
+ store.Add(publicCertificate);
+ }
+ catch (CryptographicException exception) when (exception.HResult == UserCancelledErrorCode)
+ {
+ throw new UserCancelledTrustException();
+ }
+ store.Close();
+ };
+ }
+
+ public bool IsTrusted(X509Certificate2 certificate)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return ListCertificates(CertificatePurpose.HTTPS, StoreName.Root, StoreLocation.CurrentUser, isValid: true, requireExportable: false)
+ .Any(c => c.Thumbprint == certificate.Thumbprint);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ var subjectMatch = Regex.Match(certificate.Subject, CertificateSubjectRegex, RegexOptions.Singleline, MaxRegexTimeout);
+ if (!subjectMatch.Success)
+ {
+ throw new InvalidOperationException($"Can't determine the subject for the certificate with subject '{certificate.Subject}'.");
+ }
+ var subject = subjectMatch.Groups[1].Value;
+ using (var checkTrustProcess = Process.Start(new ProcessStartInfo(
+ MacOSFindCertificateCommandLine,
+ string.Format(MacOSFindCertificateCommandLineArgumentsFormat, subject))
+ {
+ RedirectStandardOutput = true
+ }))
+ {
+ var output = checkTrustProcess.StandardOutput.ReadToEnd();
+ checkTrustProcess.WaitForExit();
+ var matches = Regex.Matches(output, MacOSFindCertificateOutputRegex, RegexOptions.Multiline, MaxRegexTimeout);
+ var hashes = matches.OfType().Select(m => m.Groups[1].Value).ToList();
+ return hashes.Any(h => string.Equals(h, certificate.Thumbprint, StringComparison.Ordinal));
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void CleanupHttpsCertificates(string subject = LocalhostHttpsDistinguishedName)
+ {
+ CleanupCertificates(CertificatePurpose.HTTPS, subject);
+ }
+
+ public void CleanupCertificates(CertificatePurpose purpose, string subject)
+ {
+ // On OS X we don't have a good way to manage trusted certificates in the system keychain
+ // so we do everything by invoking the native toolchain.
+ // This has some limitations, like for example not being able to identify our custom OID extension. For that
+ // matter, when we are cleaning up certificates on the machine, we start by removing the trusted certificates.
+ // To do this, we list the certificates that we can identify on the current user personal store and we invoke
+ // the native toolchain to remove them from the sytem keychain. Once we have removed the trusted certificates,
+ // we remove the certificates from the local user store to finish up the cleanup.
+ var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false);
+ foreach (var certificate in certificates)
+ {
+ RemoveCertificate(certificate, RemoveLocations.All);
+ }
+ }
+
+ public void RemoveAllCertificates(CertificatePurpose purpose, StoreName storeName, StoreLocation storeLocation, string subject = null)
+ {
+ var certificates = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
+ ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: false) :
+ ListCertificates(purpose, storeName, storeLocation, isValid: false);
+ var certificatesWithName = subject == null ? certificates : certificates.Where(c => c.Subject == subject);
+
+ var removeLocation = storeName == StoreName.My ? RemoveLocations.Local : RemoveLocations.Trusted;
+
+ foreach (var certificate in certificates)
+ {
+ RemoveCertificate(certificate, removeLocation);
+ }
+
+ DisposeCertificates(certificates);
+ }
+
+ private void RemoveCertificate(X509Certificate2 certificate, RemoveLocations locations)
+ {
+ switch (locations)
+ {
+ case RemoveLocations.Undefined:
+ throw new InvalidOperationException($"'{nameof(RemoveLocations.Undefined)}' is not a valid location.");
+ case RemoveLocations.Local:
+ RemoveCertificateFromUserStore(certificate);
+ break;
+ case RemoveLocations.Trusted when !RuntimeInformation.IsOSPlatform(OSPlatform.Linux):
+ RemoveCertificateFromTrustedRoots(certificate);
+ break;
+ case RemoveLocations.All:
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ RemoveCertificateFromTrustedRoots(certificate);
+ }
+ RemoveCertificateFromUserStore(certificate);
+ break;
+ default:
+ throw new InvalidOperationException("Invalid location.");
+ }
+ }
+
+ private static void RemoveCertificateFromUserStore(X509Certificate2 certificate)
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ {
+ store.Open(OpenFlags.ReadWrite);
+ var matching = store.Certificates
+ .OfType()
+ .Single(c => c.SerialNumber == certificate.SerialNumber);
+
+ store.Remove(matching);
+ store.Close();
+ }
+ }
+
+ private void RemoveCertificateFromTrustedRoots(X509Certificate2 certificate)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
+ {
+ store.Open(OpenFlags.ReadWrite);
+ var matching = store.Certificates
+ .OfType()
+ .Single(c => c.SerialNumber == certificate.SerialNumber);
+
+ store.Remove(matching);
+ store.Close();
+ }
+ }
+ else
+ {
+ if (IsTrusted(certificate)) // On OSX this check just ensures its on the system keychain
+ {
+ try
+ {
+ RemoveCertificateTrustRule(certificate);
+ }
+ catch
+ {
+ // We don't care if we fail to remove the trust rule if
+ // for some reason the certificate became untrusted.
+ // The delete command will fail if the certificate is
+ // trusted.
+ }
+ RemoveCertificateFromKeyChain(MacOSSystemKeyChain, certificate);
+ }
+ }
+ }
+
+ private static void RemoveCertificateTrustRule(X509Certificate2 certificate)
+ {
+ var certificatePath = Path.GetTempFileName();
+ try
+ {
+ var certBytes = certificate.Export(X509ContentType.Cert);
+ File.WriteAllBytes(certificatePath, certBytes);
+ var processInfo = new ProcessStartInfo(
+ MacOSRemoveCertificateTrustCommandLine,
+ string.Format(
+ MacOSRemoveCertificateTrustCommandLineArgumentsFormat,
+ certificatePath
+ ));
+ using (var process = Process.Start(processInfo))
+ {
+ process.WaitForExit();
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (File.Exists(certificatePath))
+ {
+ File.Delete(certificatePath);
+ }
+ }
+ catch
+ {
+ // We don't care about failing to do clean-up on a temp file.
+ }
+ }
+ }
+
+ private static void RemoveCertificateFromKeyChain(string keyChain, X509Certificate2 certificate)
+ {
+ var processInfo = new ProcessStartInfo(
+ MacOSDeleteCertificateCommandLine,
+ string.Format(
+ MacOSDeleteCertificateCommandLineArgumentsFormat,
+ certificate.Thumbprint.ToUpperInvariant(),
+ keyChain
+ ))
+ {
+ RedirectStandardOutput = true,
+ RedirectStandardError = true
+ };
+
+ using (var process = Process.Start(processInfo))
+ {
+ var output = process.StandardOutput.ReadToEnd() + process.StandardError.ReadToEnd();
+ process.WaitForExit();
+
+ if (process.ExitCode != 0)
+ {
+ throw new InvalidOperationException($@"There was an error removing the certificate with thumbprint '{certificate.Thumbprint}'.
+
+{output}");
+ }
+ }
+ }
+
+ public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate(
+ DateTimeOffset notBefore,
+ DateTimeOffset notAfter,
+ string path = null,
+ bool trust = false,
+ bool includePrivateKey = false,
+ string password = null,
+ string subject = LocalhostHttpsDistinguishedName)
+ {
+ return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.HTTPS, path, trust, includePrivateKey, password, subject);
+ }
+
+ public EnsureCertificateResult EnsureAspNetCoreApplicationTokensDevelopmentCertificate(
+ DateTimeOffset notBefore,
+ DateTimeOffset notAfter,
+ string path = null,
+ bool trust = false,
+ bool includePrivateKey = false,
+ string password = null,
+ string subject = IdentityDistinguishedName)
+ {
+ return EnsureValidCertificateExists(notBefore, notAfter, CertificatePurpose.Signing, path, trust, includePrivateKey, password, subject);
+ }
+
+ public EnsureCertificateResult EnsureValidCertificateExists(
+ DateTimeOffset notBefore,
+ DateTimeOffset notAfter,
+ CertificatePurpose purpose,
+ string path = null,
+ bool trust = false,
+ bool includePrivateKey = false,
+ string password = null,
+ string subjectOverride = null)
+ {
+ if (purpose == CertificatePurpose.All)
+ {
+ throw new ArgumentException("The certificate must have a specific purpose.");
+ }
+
+ var certificates = ListCertificates(purpose, StoreName.My, StoreLocation.CurrentUser, isValid: true).Concat(
+ ListCertificates(purpose, StoreName.My, StoreLocation.LocalMachine, isValid: true));
+
+ certificates = subjectOverride == null ? certificates : certificates.Where(c => c.Subject == subjectOverride);
+
+ var result = EnsureCertificateResult.Succeeded;
+
+ X509Certificate2 certificate = null;
+ if (certificates.Count() > 0)
+ {
+ certificate = certificates.FirstOrDefault();
+ result = EnsureCertificateResult.ValidCertificatePresent;
+ }
+ else
+ {
+ try
+ {
+ switch (purpose)
+ {
+ case CertificatePurpose.All:
+ throw new InvalidOperationException("The certificate must have a specific purpose.");
+ case CertificatePurpose.HTTPS:
+ certificate = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter, subjectOverride);
+ break;
+ case CertificatePurpose.Signing:
+ certificate = CreateApplicationTokenSigningDevelopmentCertificate(notBefore, notAfter, subjectOverride);
+ break;
+ default:
+ throw new InvalidOperationException("The certificate must have a purpose.");
+ }
+ }
+ catch
+ {
+ return EnsureCertificateResult.ErrorCreatingTheCertificate;
+ }
+
+ try
+ {
+ certificate = SaveCertificateInStore(certificate, StoreName.My, StoreLocation.CurrentUser);
+ }
+ catch
+ {
+ return EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore;
+ }
+ }
+ if (path != null)
+ {
+ try
+ {
+ ExportCertificate(certificate, path, includePrivateKey, password);
+ }
+ catch
+ {
+ return EnsureCertificateResult.ErrorExportingTheCertificate;
+ }
+ }
+
+ if ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) && trust)
+ {
+ try
+ {
+ TrustCertificate(certificate);
+ }
+ catch (UserCancelledTrustException)
+ {
+ return EnsureCertificateResult.UserCancelledTrustStep;
+ }
+ catch
+ {
+ return EnsureCertificateResult.FailedToTrustTheCertificate;
+ }
+ }
+
+ return result;
+ }
+
+ private class UserCancelledTrustException : Exception
+ {
+ }
+
+ private enum RemoveLocations
+ {
+ Undefined,
+ Local,
+ Trusted,
+ All
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/CertificateGeneration/CertificatePurpose.cs b/src/Shared/CertificateGeneration/CertificatePurpose.cs
new file mode 100644
index 0000000000..1ad1a6d79b
--- /dev/null
+++ b/src/Shared/CertificateGeneration/CertificatePurpose.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.
+
+namespace Microsoft.AspNetCore.Certificates.Generation
+{
+ internal enum CertificatePurpose
+ {
+ All,
+ HTTPS,
+ Signing
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs
new file mode 100644
index 0000000000..d3c86ce05d
--- /dev/null
+++ b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs
@@ -0,0 +1,20 @@
+// 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.
+
+#if NETCOREAPP2_0 || NETCOREAPP2_1
+
+namespace Microsoft.AspNetCore.Certificates.Generation
+{
+ internal enum EnsureCertificateResult
+ {
+ Succeeded = 1,
+ ValidCertificatePresent,
+ ErrorCreatingTheCertificate,
+ ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore,
+ ErrorExportingTheCertificate,
+ FailedToTrustTheCertificate,
+ UserCancelledTrustStep
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs b/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs
new file mode 100644
index 0000000000..f234c2edbc
--- /dev/null
+++ b/src/Shared/ClosedGenericMatcher/ClosedGenericMatcher.cs
@@ -0,0 +1,106 @@
+// 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.Linq;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Internal
+{
+ ///
+ /// Helper related to generic interface definitions and implementing classes.
+ ///
+ internal static class ClosedGenericMatcher
+ {
+ ///
+ /// Determine whether is or implements a closed generic
+ /// created from .
+ ///
+ /// The of interest.
+ /// The open generic to match. Usually an interface.
+ ///
+ /// The closed generic created from that
+ /// is or implements. null if the two s have no such
+ /// relationship.
+ ///
+ ///
+ /// This method will return if is
+ /// typeof(KeyValuePair{,}), and is
+ /// typeof(KeyValuePair{string, object}).
+ ///
+ public static Type ExtractGenericInterface(Type queryType, Type interfaceType)
+ {
+ if (queryType == null)
+ {
+ throw new ArgumentNullException(nameof(queryType));
+ }
+
+ if (interfaceType == null)
+ {
+ throw new ArgumentNullException(nameof(interfaceType));
+ }
+
+ if (IsGenericInstantiation(queryType, interfaceType))
+ {
+ // queryType matches (i.e. is a closed generic type created from) the open generic type.
+ return queryType;
+ }
+
+ // Otherwise check all interfaces the type implements for a match.
+ // - If multiple different generic instantiations exists, we want the most derived one.
+ // - If that doesn't break the tie, then we sort alphabetically so that it's deterministic.
+ //
+ // We do this by looking at interfaces on the type, and recursing to the base type
+ // if we don't find any matches.
+ return GetGenericInstantiation(queryType, interfaceType);
+ }
+
+ private static bool IsGenericInstantiation(Type candidate, Type interfaceType)
+ {
+ return
+ candidate.GetTypeInfo().IsGenericType &&
+ candidate.GetGenericTypeDefinition() == interfaceType;
+ }
+
+ private static Type GetGenericInstantiation(Type queryType, Type interfaceType)
+ {
+ Type bestMatch = null;
+ var interfaces = queryType.GetInterfaces();
+ foreach (var @interface in interfaces)
+ {
+ if (IsGenericInstantiation(@interface, interfaceType))
+ {
+ if (bestMatch == null)
+ {
+ bestMatch = @interface;
+ }
+ else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0)
+ {
+ bestMatch = @interface;
+ }
+ else
+ {
+ // There are two matches at this level of the class hierarchy, but @interface is after
+ // bestMatch in the sort order.
+ }
+ }
+ }
+
+ if (bestMatch != null)
+ {
+ return bestMatch;
+ }
+
+ // BaseType will be null for object and interfaces, which means we've reached 'bottom'.
+ var baseType = queryType?.GetTypeInfo().BaseType;
+ if (baseType == null)
+ {
+ return null;
+ }
+ else
+ {
+ return GetGenericInstantiation(baseType, interfaceType);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs
new file mode 100644
index 0000000000..1408059ad9
--- /dev/null
+++ b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionary.cs
@@ -0,0 +1,155 @@
+// 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;
+using System.Collections.Generic;
+
+namespace Microsoft.Extensions.Internal
+{
+ internal class CopyOnWriteDictionary : IDictionary
+ {
+ private readonly IDictionary _sourceDictionary;
+ private readonly IEqualityComparer _comparer;
+ private IDictionary _innerDictionary;
+
+ public CopyOnWriteDictionary(
+ IDictionary sourceDictionary,
+ IEqualityComparer comparer)
+ {
+ if (sourceDictionary == null)
+ {
+ throw new ArgumentNullException(nameof(sourceDictionary));
+ }
+
+ if (comparer == null)
+ {
+ throw new ArgumentNullException(nameof(comparer));
+ }
+
+ _sourceDictionary = sourceDictionary;
+ _comparer = comparer;
+ }
+
+ private IDictionary ReadDictionary
+ {
+ get
+ {
+ return _innerDictionary ?? _sourceDictionary;
+ }
+ }
+
+ private IDictionary WriteDictionary
+ {
+ get
+ {
+ if (_innerDictionary == null)
+ {
+ _innerDictionary = new Dictionary(_sourceDictionary,
+ _comparer);
+ }
+
+ return _innerDictionary;
+ }
+ }
+
+ public virtual ICollection Keys
+ {
+ get
+ {
+ return ReadDictionary.Keys;
+ }
+ }
+
+ public virtual ICollection Values
+ {
+ get
+ {
+ return ReadDictionary.Values;
+ }
+ }
+
+ public virtual int Count
+ {
+ get
+ {
+ return ReadDictionary.Count;
+ }
+ }
+
+ public virtual bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public virtual TValue this[TKey key]
+ {
+ get
+ {
+ return ReadDictionary[key];
+ }
+ set
+ {
+ WriteDictionary[key] = value;
+ }
+ }
+
+ public virtual bool ContainsKey(TKey key)
+ {
+ return ReadDictionary.ContainsKey(key);
+ }
+
+ public virtual void Add(TKey key, TValue value)
+ {
+ WriteDictionary.Add(key, value);
+ }
+
+ public virtual bool Remove(TKey key)
+ {
+ return WriteDictionary.Remove(key);
+ }
+
+ public virtual bool TryGetValue(TKey key, out TValue value)
+ {
+ return ReadDictionary.TryGetValue(key, out value);
+ }
+
+ public virtual void Add(KeyValuePair item)
+ {
+ WriteDictionary.Add(item);
+ }
+
+ public virtual void Clear()
+ {
+ WriteDictionary.Clear();
+ }
+
+ public virtual bool Contains(KeyValuePair item)
+ {
+ return ReadDictionary.Contains(item);
+ }
+
+ public virtual void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ ReadDictionary.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(KeyValuePair item)
+ {
+ return WriteDictionary.Remove(item);
+ }
+
+ public virtual IEnumerator> GetEnumerator()
+ {
+ return ReadDictionary.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs
new file mode 100644
index 0000000000..7cd935e940
--- /dev/null
+++ b/src/Shared/CopyOnWriteDictionary/CopyOnWriteDictionaryHolder.cs
@@ -0,0 +1,166 @@
+// 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;
+
+namespace Microsoft.Extensions.Internal
+{
+ internal struct CopyOnWriteDictionaryHolder
+ {
+ private readonly Dictionary _source;
+ private Dictionary _copy;
+
+ public CopyOnWriteDictionaryHolder(Dictionary source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ _source = source;
+ _copy = null;
+ }
+
+ public CopyOnWriteDictionaryHolder(CopyOnWriteDictionaryHolder source)
+ {
+ _source = source._copy ?? source._source;
+ _copy = null;
+ }
+
+ public bool HasBeenCopied => _copy != null;
+
+ public Dictionary ReadDictionary
+ {
+ get
+ {
+ if (_copy != null)
+ {
+ return _copy;
+ }
+ else if (_source != null)
+ {
+ return _source;
+ }
+ else
+ {
+ // Default-Constructor case
+ _copy = new Dictionary();
+ return _copy;
+ }
+ }
+ }
+
+ public Dictionary WriteDictionary
+ {
+ get
+ {
+ if (_copy == null && _source == null)
+ {
+ // Default-Constructor case
+ _copy = new Dictionary();
+ }
+ else if (_copy == null)
+ {
+ _copy = new Dictionary(_source, _source.Comparer);
+ }
+
+ return _copy;
+ }
+ }
+
+ public Dictionary.KeyCollection Keys
+ {
+ get
+ {
+ return ReadDictionary.Keys;
+ }
+ }
+
+ public Dictionary.ValueCollection Values
+ {
+ get
+ {
+ return ReadDictionary.Values;
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return ReadDictionary.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return ReadDictionary[key];
+ }
+ set
+ {
+ WriteDictionary[key] = value;
+ }
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return ReadDictionary.ContainsKey(key);
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ WriteDictionary.Add(key, value);
+ }
+
+ public bool Remove(TKey key)
+ {
+ return WriteDictionary.Remove(key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return ReadDictionary.TryGetValue(key, out value);
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ ((ICollection>)WriteDictionary).Add(item);
+ }
+
+ public void Clear()
+ {
+ WriteDictionary.Clear();
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return ((ICollection>)ReadDictionary).Contains(item);
+ }
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ ((ICollection>)ReadDictionary).CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(KeyValuePair item)
+ {
+ return ((ICollection>)WriteDictionary).Remove(item);
+ }
+
+ public Dictionary.Enumerator GetEnumerator()
+ {
+ return ReadDictionary.GetEnumerator();
+ }
+ }
+}
diff --git a/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs
new file mode 100644
index 0000000000..431b83a6e5
--- /dev/null
+++ b/src/Shared/ObjectMethodExecutor/AwaitableInfo.cs
@@ -0,0 +1,127 @@
+// 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.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Microsoft.Extensions.Internal
+{
+ internal struct AwaitableInfo
+ {
+ public Type AwaiterType { get; }
+ public PropertyInfo AwaiterIsCompletedProperty { get; }
+ public MethodInfo AwaiterGetResultMethod { get; }
+ public MethodInfo AwaiterOnCompletedMethod { get; }
+ public MethodInfo AwaiterUnsafeOnCompletedMethod { get; }
+ public Type ResultType { get; }
+ public MethodInfo GetAwaiterMethod { get; }
+
+ public AwaitableInfo(
+ Type awaiterType,
+ PropertyInfo awaiterIsCompletedProperty,
+ MethodInfo awaiterGetResultMethod,
+ MethodInfo awaiterOnCompletedMethod,
+ MethodInfo awaiterUnsafeOnCompletedMethod,
+ Type resultType,
+ MethodInfo getAwaiterMethod)
+ {
+ AwaiterType = awaiterType;
+ AwaiterIsCompletedProperty = awaiterIsCompletedProperty;
+ AwaiterGetResultMethod = awaiterGetResultMethod;
+ AwaiterOnCompletedMethod = awaiterOnCompletedMethod;
+ AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod;
+ ResultType = resultType;
+ GetAwaiterMethod = getAwaiterMethod;
+ }
+
+ public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo)
+ {
+ // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347
+
+ // Awaitable must have method matching "object GetAwaiter()"
+ var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m =>
+ m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase)
+ && m.GetParameters().Length == 0
+ && m.ReturnType != null);
+ if (getAwaiterMethod == null)
+ {
+ awaitableInfo = default(AwaitableInfo);
+ return false;
+ }
+
+ var awaiterType = getAwaiterMethod.ReturnType;
+
+ // Awaiter must have property matching "bool IsCompleted { get; }"
+ var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p =>
+ p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase)
+ && p.PropertyType == typeof(bool)
+ && p.GetMethod != null);
+ if (isCompletedProperty == null)
+ {
+ awaitableInfo = default(AwaitableInfo);
+ return false;
+ }
+
+ // Awaiter must implement INotifyCompletion
+ var awaiterInterfaces = awaiterType.GetInterfaces();
+ var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion));
+ if (!implementsINotifyCompletion)
+ {
+ awaitableInfo = default(AwaitableInfo);
+ return false;
+ }
+
+ // INotifyCompletion supplies a method matching "void OnCompleted(Action action)"
+ var iNotifyCompletionMap = awaiterType
+ .GetTypeInfo()
+ .GetRuntimeInterfaceMap(typeof(INotifyCompletion));
+ var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m =>
+ m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase)
+ && m.ReturnType == typeof(void)
+ && m.GetParameters().Length == 1
+ && m.GetParameters()[0].ParameterType == typeof(Action));
+
+ // Awaiter optionally implements ICriticalNotifyCompletion
+ var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion));
+ MethodInfo unsafeOnCompletedMethod;
+ if (implementsICriticalNotifyCompletion)
+ {
+ // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)"
+ var iCriticalNotifyCompletionMap = awaiterType
+ .GetTypeInfo()
+ .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion));
+ unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m =>
+ m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase)
+ && m.ReturnType == typeof(void)
+ && m.GetParameters().Length == 1
+ && m.GetParameters()[0].ParameterType == typeof(Action));
+ }
+ else
+ {
+ unsafeOnCompletedMethod = null;
+ }
+
+ // Awaiter must have method matching "void GetResult" or "T GetResult()"
+ var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m =>
+ m.Name.Equals("GetResult")
+ && m.GetParameters().Length == 0);
+ if (getResultMethod == null)
+ {
+ awaitableInfo = default(AwaitableInfo);
+ return false;
+ }
+
+ awaitableInfo = new AwaitableInfo(
+ awaiterType,
+ isCompletedProperty,
+ getResultMethod,
+ onCompletedMethod,
+ unsafeOnCompletedMethod,
+ getResultMethod.ReturnType,
+ getAwaiterMethod);
+ return true;
+ }
+ }
+}
diff --git a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs
new file mode 100644
index 0000000000..4e48ef09a1
--- /dev/null
+++ b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs
@@ -0,0 +1,55 @@
+// 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.Linq.Expressions;
+
+namespace Microsoft.Extensions.Internal
+{
+ internal struct CoercedAwaitableInfo
+ {
+ public AwaitableInfo AwaitableInfo { get; }
+ public Expression CoercerExpression { get; }
+ public Type CoercerResultType { get; }
+ public bool RequiresCoercion => CoercerExpression != null;
+
+ public CoercedAwaitableInfo(AwaitableInfo awaitableInfo)
+ {
+ AwaitableInfo = awaitableInfo;
+ CoercerExpression = null;
+ CoercerResultType = null;
+ }
+
+ public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo)
+ {
+ CoercerExpression = coercerExpression;
+ CoercerResultType = coercerResultType;
+ AwaitableInfo = coercedAwaitableInfo;
+ }
+
+ public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info)
+ {
+ if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo))
+ {
+ info = new CoercedAwaitableInfo(directlyAwaitableInfo);
+ return true;
+ }
+
+ // It's not directly awaitable, but maybe we can coerce it.
+ // Currently we support coercing FSharpAsync.
+ if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type,
+ out var coercerExpression,
+ out var coercerResultType))
+ {
+ if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo))
+ {
+ info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo);
+ return true;
+ }
+ }
+
+ info = default(CoercedAwaitableInfo);
+ return false;
+ }
+ }
+}
diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs
new file mode 100644
index 0000000000..f8e5b70f0d
--- /dev/null
+++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs
@@ -0,0 +1,340 @@
+// 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.Expressions;
+using System.Reflection;
+
+namespace Microsoft.Extensions.Internal
+{
+ internal class ObjectMethodExecutor
+ {
+ private readonly object[] _parameterDefaultValues;
+ private readonly MethodExecutorAsync _executorAsync;
+ private readonly MethodExecutor _executor;
+
+ private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor =
+ typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] {
+ typeof(object), // customAwaitable
+ typeof(Func