Merge aspnet/DataProtection release/2.2 into this repo

This commit is contained in:
Nate McMaster 2018-10-15 10:21:02 -07:00
commit 489a88d21e
No known key found for this signature in database
GPG Key ID: A778D9601BD78810
362 changed files with 32488 additions and 20 deletions

View File

@ -15,3 +15,14 @@ phases:
- template: .vsts-pipelines/templates/project-ci.yml@buildtools
parameters:
buildArgs: "/t:CheckUniverse"
- phase: DataProtection
queue: Hosted VS2017
steps:
- script: src/DataProtection/build.cmd -ci
displayName: Run src/DataProtection/build.cmd
- task: PublishTestResults@2
displayName: Publish test results
condition: always()
inputs:
testRunner: vstest
testResultsFiles: 'src/DataProtection/artifacts/logs/**/*.trx'

4
.gitmodules vendored
View File

@ -38,10 +38,6 @@
path = modules/CORS
url = https://github.com/aspnet/CORS.git
branch = release/2.2
[submodule "modules/DataProtection"]
path = modules/DataProtection
url = https://github.com/aspnet/DataProtection.git
branch = release/2.2
[submodule "modules/DependencyInjection"]
path = modules/DependencyInjection
url = https://github.com/aspnet/DependencyInjection.git

View File

@ -11,6 +11,9 @@
<Target Name="GetRepoBatches" DependsOnTargets="GeneratePropsFiles;ComputeGraph">
<ItemGroup>
<RepositoryBuildOrder Condition="'%(RootPath)' == ''">
<RootPath>$(SubmoduleRoot)%(Identity)\</RootPath>
</RepositoryBuildOrder>
<BatchedRepository Include="$(MSBuildProjectFullPath)">
<BuildGroup>%(RepositoryBuildOrder.Order)</BuildGroup>
<Repository>%(RepositoryBuildOrder.Identity)</Repository>

View File

@ -1,4 +1,11 @@
<Project>
<ItemDefinitionGroup>
<RepositoryBuildOrder>
<Order></Order>
<RootPath></RootPath>
</RepositoryBuildOrder>
</ItemDefinitionGroup>
<ItemGroup>
<RepositoryBuildOrder Include="Common" Order="1" />
<RepositoryBuildOrder Include="Microsoft.Data.Sqlite" Order="1" />
@ -20,7 +27,7 @@
<RepositoryBuildOrder Include="EntityFrameworkCore" Order="8" />
<RepositoryBuildOrder Include="HttpSysServer" Order="8" />
<RepositoryBuildOrder Include="BrowserLink" Order="8" />
<RepositoryBuildOrder Include="DataProtection" Order="9" />
<RepositoryBuildOrder Include="DataProtection" Order="9" RootPath="$(RepositoryRoot)src\DataProtection\" />
<RepositoryBuildOrder Include="BasicMiddleware" Order="9" />
<RepositoryBuildOrder Include="Antiforgery" Order="10" />
<RepositoryBuildOrder Include="IISIntegration" Order="10" />
@ -46,7 +53,5 @@
<RepositoryBuildOrder Include="SignalR" Order="16" />
<RepositoryBuildOrder Include="AuthSamples" Order="16" />
<RepositoryBuildOrder Include="Templating" Order="17" />
<RepositoryBuildOrder Update="@(RepositoryBuildOrder)" RootPath="$(SubmoduleRoot)%(Identity)" />
</ItemGroup>
</Project>

View File

@ -45,7 +45,7 @@
<Repository Include="Common" />
<Repository Include="Configuration" />
<Repository Include="CORS" />
<Repository Include="DataProtection" />
<Repository Include="DataProtection" RootPath="$(RepositoryRoot)src\DataProtection\" />
<Repository Include="DependencyInjection" />
<Repository Include="Diagnostics" />
<Repository Include="DotNetTools" />

@ -1 +0,0 @@
Subproject commit 3f5f419df8094be0203298a8001f721517d18a2c

15
run.ps1
View File

@ -14,6 +14,9 @@ The KoreBuild command to run.
.PARAMETER Path
The folder to build. Defaults to the folder containing this script.
.PARAMETER LockFile
The path to the korebuild-lock.txt file. Defaults to $Path/korebuild-lock.txt
.PARAMETER Channel
The channel of KoreBuild to download. Overrides the value from the config file.
@ -75,6 +78,7 @@ param(
[Parameter(Mandatory=$true, Position = 0)]
[string]$Command,
[string]$Path = $PSScriptRoot,
[string]$LockFile,
[Alias('c')]
[string]$Channel,
[Alias('d')]
@ -104,15 +108,13 @@ $ErrorActionPreference = 'Stop'
function Get-KoreBuild {
$lockFile = Join-Path $Path 'korebuild-lock.txt'
if (!(Test-Path $lockFile) -or $Update) {
Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile
if (!(Test-Path $LockFile) -or $Update) {
Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $LockFile
}
$version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1
$version = Get-Content $LockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1
if (!$version) {
Write-Error "Failed to parse version from $lockFile. Expected a line that begins with 'version:'"
Write-Error "Failed to parse version from $LockFile. Expected a line that begins with 'version:'"
}
$version = $version.TrimStart('version:').Trim()
$korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version)
@ -207,6 +209,7 @@ if (!$DotNetHome) {
else { Join-Path $PSScriptRoot '.dotnet'}
}
if (!$LockFile) { $LockFile = Join-Path $Path 'korebuild-lock.txt' }
if (!$Channel) { $Channel = 'master' }
if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' }

17
run.sh
View File

@ -15,6 +15,7 @@ verbose=false
update=false
reinstall=false
repo_path="$DIR"
lockfile_path=''
channel=''
tools_source=''
ci=false
@ -41,6 +42,7 @@ __usage() {
echo " --config-file <FILE> The path to the configuration file that stores values. Defaults to korebuild.json."
echo " -d|--dotnet-home <DIR> The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet."
echo " --path <PATH> The directory to build. Defaults to the directory containing the script."
echo " --lockfile <PATH> The path to the korebuild-lock.txt file. Defaults to \$repo_path/korebuild-lock.txt"
echo " -s|--tools-source|-ToolsSource <URL> The base url where build tools can be downloaded. Overrides the value from the config file."
echo " --package-version-props-url <URL> The url of the package versions props path containing dependency versions."
echo " --access-token <Token> The query string to append to any blob store access for PackageVersionPropsUrl, if any."
@ -61,13 +63,12 @@ __usage() {
get_korebuild() {
local version
local lock_file="$repo_path/korebuild-lock.txt"
if [ ! -f "$lock_file" ] || [ "$update" = true ]; then
__get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file"
if [ ! -f "$lockfile_path" ] || [ "$update" = true ]; then
__get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lockfile_path"
fi
version="$(grep 'version:*' -m 1 "$lock_file")"
version="$(grep 'version:*' -m 1 "$lockfile_path")"
if [[ "$version" == '' ]]; then
__error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'"
__error "Failed to parse version from $lockfile_path. Expected a line that begins with 'version:'"
return 1
fi
version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
@ -176,6 +177,11 @@ while [[ $# -gt 0 ]]; do
repo_path="${1:-}"
[ -z "$repo_path" ] && __error "Missing value for parameter --path" && __usage
;;
--[Ll]ock[Ff]ile)
shift
lockfile_path="${1:-}"
[ -z "$lockfile_path" ] && __error "Missing value for parameter --lockfile" && __usage
;;
-s|--tools-source|-ToolsSource)
shift
tools_source="${1:-}"
@ -296,6 +302,7 @@ if [ ! -z "$product_build_id" ]; then
msbuild_args[${#msbuild_args[*]}]="-p:DotNetProductBuildId=$product_build_id"
fi
[ -z "$lockfile_path" ] && lockfile_path="$repo_path/korebuild-lock.txt"
[ -z "$channel" ] && channel='master'
[ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools'

View File

@ -0,0 +1,333 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26814.1
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{60336AB3-948D-4D15-A5FB-F32A2B91E814}"
ProjectSection(SolutionItems) = preProject
test\CreateTestCert.ps1 = test\CreateTestCert.ps1
test\Directory.Build.props = test\Directory.Build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5A3A5DE3-49AD-431C-971D-B01B62D94AE2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E1D86B1B-41D8-43C9-97FD-C2BF65C414E2}"
ProjectSection(SolutionItems) = preProject
.appveyor.yml = .appveyor.yml
.gitattributes = .gitattributes
.gitignore = .gitignore
.travis.yml = .travis.yml
CONTRIBUTING.md = CONTRIBUTING.md
build\dependencies.props = build\dependencies.props
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
korebuild.json = korebuild.json
LICENSE.txt = LICENSE.txt
NuGet.config = NuGet.config
NuGetPackageVerifier.json = NuGetPackageVerifier.json
Provision-AutoGenKeys.ps1 = Provision-AutoGenKeys.ps1
README.md = README.md
version.props = version.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection", "src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj", "{1E570CD4-6F12-44F4-961E-005EE2002BC2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Test", "test\Microsoft.AspNetCore.DataProtection.Test\Microsoft.AspNetCore.DataProtection.Test.csproj", "{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.Internal", "src\Microsoft.AspNetCore.Cryptography.Internal\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{E2779976-A28C-4365-A4BB-4AD854FAF23E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation", "src\Microsoft.AspNetCore.Cryptography.KeyDerivation\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj", "{421F0383-34B1-402D-807B-A94542513ABA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation.Test", "test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test.csproj", "{42C97F52-8D56-46BD-A712-4F22BED157A7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.Internal.Test", "test\Microsoft.AspNetCore.Cryptography.Internal.Test\Microsoft.AspNetCore.Cryptography.Internal.Test.csproj", "{37053D5F-5B61-47CE-8B72-298CE007FFB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "src\Microsoft.AspNetCore.DataProtection.Abstractions\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{4B115BDE-B253-46A6-97BF-A8B37B344FF2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions.Test", "test\Microsoft.AspNetCore.DataProtection.Abstractions.Test\Microsoft.AspNetCore.DataProtection.Abstractions.Test.csproj", "{FF650A69-DEE4-4B36-9E30-264EE7CFB478}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.SystemWeb", "src\Microsoft.AspNetCore.DataProtection.SystemWeb\Microsoft.AspNetCore.DataProtection.SystemWeb.csproj", "{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Extensions.Test", "test\Microsoft.AspNetCore.DataProtection.Extensions.Test\Microsoft.AspNetCore.DataProtection.Extensions.Test.csproj", "{04AA8E60-A053-4D50-89FE-E76C3DF45200}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Extensions", "src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj", "{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureStorage", "src\Microsoft.AspNetCore.DataProtection.AzureStorage\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj", "{CC799B57-81E2-4F45-8A32-0D5F49753C3F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureBlob", "samples\AzureBlob\AzureBlob.csproj", "{B07435B3-CD81-4E3B-88A5-6384821E1C01}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureStorage.Test", "test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test.csproj", "{8C41240E-48F8-402F-9388-74CFE27F4D76}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Redis", "samples\Redis\Redis.csproj", "{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NonDISample", "samples\NonDISample\NonDISample.csproj", "{32CF970B-E2F1-4CD9-8DB3-F5715475373A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyManagementSample", "samples\KeyManagementSample\KeyManagementSample.csproj", "{6E066F8D-2910-404F-8949-F58125E28495}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomEncryptorSample", "samples\CustomEncryptorSample\CustomEncryptorSample.csproj", "{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureKeyVault", "src\Microsoft.AspNetCore.DataProtection.AzureKeyVault\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj", "{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureKeyVault", "samples\AzureKeyVault\AzureKeyVault.csproj", "{295E8539-5450-4764-B3F5-51F968628022}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test", "test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test.csproj", "{C85ED942-8121-453F-8308-9DB730843B63}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test", "test\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj", "{06728BF2-C5EB-44C7-9F30-14FAA5649E14}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.EntityFrameworkCore", "src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj", "{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCoreSample", "samples\EntityFrameworkCoreSample\EntityFrameworkCoreSample.csproj", "{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.StackExchangeRedis", "src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj", "{57713B23-CCAB-44DB-A08D-55F9D236D05B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Test", "test\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Test\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Test.csproj", "{33BB1B86-64BF-45BB-A334-3E1A4802253C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1E570CD4-6F12-44F4-961E-005EE2002BC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E570CD4-6F12-44F4-961E-005EE2002BC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E570CD4-6F12-44F4-961E-005EE2002BC2}.Debug|x86.ActiveCfg = Debug|Any CPU
{1E570CD4-6F12-44F4-961E-005EE2002BC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E570CD4-6F12-44F4-961E-005EE2002BC2}.Release|Any CPU.Build.0 = Release|Any CPU
{1E570CD4-6F12-44F4-961E-005EE2002BC2}.Release|x86.ActiveCfg = Release|Any CPU
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Debug|x86.ActiveCfg = Debug|Any CPU
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Release|Any CPU.Build.0 = Release|Any CPU
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Release|x86.ActiveCfg = Release|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Debug|x86.Build.0 = Debug|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Release|Any CPU.Build.0 = Release|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Release|x86.ActiveCfg = Release|Any CPU
{E2779976-A28C-4365-A4BB-4AD854FAF23E}.Release|x86.Build.0 = Release|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Debug|x86.ActiveCfg = Debug|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Debug|x86.Build.0 = Debug|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Release|Any CPU.Build.0 = Release|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Release|x86.ActiveCfg = Release|Any CPU
{421F0383-34B1-402D-807B-A94542513ABA}.Release|x86.Build.0 = Release|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Debug|x86.ActiveCfg = Debug|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Debug|x86.Build.0 = Debug|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Release|Any CPU.Build.0 = Release|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Release|x86.ActiveCfg = Release|Any CPU
{42C97F52-8D56-46BD-A712-4F22BED157A7}.Release|x86.Build.0 = Release|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Debug|x86.ActiveCfg = Debug|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Debug|x86.Build.0 = Debug|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|Any CPU.Build.0 = Release|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|x86.ActiveCfg = Release|Any CPU
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|x86.Build.0 = Release|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|x86.ActiveCfg = Debug|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|x86.Build.0 = Debug|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|Any CPU.Build.0 = Release|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|x86.ActiveCfg = Release|Any CPU
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|x86.Build.0 = Release|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|x86.ActiveCfg = Debug|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|x86.Build.0 = Debug|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|Any CPU.Build.0 = Release|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|x86.ActiveCfg = Release|Any CPU
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|x86.Build.0 = Release|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|x86.ActiveCfg = Debug|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|x86.Build.0 = Debug|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|Any CPU.Build.0 = Release|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|x86.ActiveCfg = Release|Any CPU
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|x86.Build.0 = Release|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Debug|x86.ActiveCfg = Debug|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Debug|x86.Build.0 = Debug|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Release|Any CPU.Build.0 = Release|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Release|x86.ActiveCfg = Release|Any CPU
{04AA8E60-A053-4D50-89FE-E76C3DF45200}.Release|x86.Build.0 = Release|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Debug|x86.ActiveCfg = Debug|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Debug|x86.Build.0 = Debug|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Release|Any CPU.Build.0 = Release|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Release|x86.ActiveCfg = Release|Any CPU
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}.Release|x86.Build.0 = Release|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Debug|x86.ActiveCfg = Debug|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Debug|x86.Build.0 = Debug|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Release|Any CPU.Build.0 = Release|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Release|x86.ActiveCfg = Release|Any CPU
{CC799B57-81E2-4F45-8A32-0D5F49753C3F}.Release|x86.Build.0 = Release|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Debug|x86.ActiveCfg = Debug|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Debug|x86.Build.0 = Debug|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Release|Any CPU.Build.0 = Release|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Release|x86.ActiveCfg = Release|Any CPU
{B07435B3-CD81-4E3B-88A5-6384821E1C01}.Release|x86.Build.0 = Release|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Debug|x86.ActiveCfg = Debug|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Debug|x86.Build.0 = Debug|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Release|Any CPU.Build.0 = Release|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Release|x86.ActiveCfg = Release|Any CPU
{8C41240E-48F8-402F-9388-74CFE27F4D76}.Release|x86.Build.0 = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Debug|x86.ActiveCfg = Debug|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Debug|x86.Build.0 = Debug|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|Any CPU.Build.0 = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|x86.ActiveCfg = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|x86.Build.0 = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|x86.ActiveCfg = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|x86.Build.0 = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|Any CPU.Build.0 = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|x86.ActiveCfg = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|x86.Build.0 = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|x86.ActiveCfg = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|x86.Build.0 = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|Any CPU.Build.0 = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|x86.ActiveCfg = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|x86.Build.0 = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|x86.ActiveCfg = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|x86.Build.0 = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|Any CPU.Build.0 = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|x86.ActiveCfg = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|x86.Build.0 = Release|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Debug|x86.ActiveCfg = Debug|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Debug|x86.Build.0 = Debug|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Release|Any CPU.Build.0 = Release|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Release|x86.ActiveCfg = Release|Any CPU
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9}.Release|x86.Build.0 = Release|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Debug|Any CPU.Build.0 = Debug|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Debug|x86.ActiveCfg = Debug|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Debug|x86.Build.0 = Debug|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Release|Any CPU.ActiveCfg = Release|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Release|Any CPU.Build.0 = Release|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Release|x86.ActiveCfg = Release|Any CPU
{295E8539-5450-4764-B3F5-51F968628022}.Release|x86.Build.0 = Release|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Debug|x86.ActiveCfg = Debug|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Debug|x86.Build.0 = Debug|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Release|Any CPU.Build.0 = Release|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Release|x86.ActiveCfg = Release|Any CPU
{C85ED942-8121-453F-8308-9DB730843B63}.Release|x86.Build.0 = Release|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|x86.ActiveCfg = Debug|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Debug|x86.Build.0 = Debug|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|Any CPU.Build.0 = Release|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|x86.ActiveCfg = Release|Any CPU
{06728BF2-C5EB-44C7-9F30-14FAA5649E14}.Release|x86.Build.0 = Release|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|x86.ActiveCfg = Debug|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Debug|x86.Build.0 = Debug|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|Any CPU.Build.0 = Release|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|x86.ActiveCfg = Release|Any CPU
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03}.Release|x86.Build.0 = Release|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|x86.ActiveCfg = Debug|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Debug|x86.Build.0 = Debug|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|Any CPU.Build.0 = Release|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|x86.ActiveCfg = Release|Any CPU
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76}.Release|x86.Build.0 = Release|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Debug|x86.ActiveCfg = Debug|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Debug|x86.Build.0 = Debug|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Release|Any CPU.Build.0 = Release|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Release|x86.ActiveCfg = Release|Any CPU
{57713B23-CCAB-44DB-A08D-55F9D236D05B}.Release|x86.Build.0 = Release|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Debug|x86.ActiveCfg = Debug|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Debug|x86.Build.0 = Debug|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Release|Any CPU.Build.0 = Release|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Release|x86.ActiveCfg = Release|Any CPU
{33BB1B86-64BF-45BB-A334-3E1A4802253C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1E570CD4-6F12-44F4-961E-005EE2002BC2} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{E2779976-A28C-4365-A4BB-4AD854FAF23E} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{421F0383-34B1-402D-807B-A94542513ABA} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{42C97F52-8D56-46BD-A712-4F22BED157A7} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{37053D5F-5B61-47CE-8B72-298CE007FFB0} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{4B115BDE-B253-46A6-97BF-A8B37B344FF2} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{FF650A69-DEE4-4B36-9E30-264EE7CFB478} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{04AA8E60-A053-4D50-89FE-E76C3DF45200} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{BF8681DB-C28B-441F-BD92-0DCFE9537A9F} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{CC799B57-81E2-4F45-8A32-0D5F49753C3F} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{B07435B3-CD81-4E3B-88A5-6384821E1C01} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{8C41240E-48F8-402F-9388-74CFE27F4D76} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{32CF970B-E2F1-4CD9-8DB3-F5715475373A} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{6E066F8D-2910-404F-8949-F58125E28495} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{4E76B2A8-9DC3-46E6-B5FC-097A1D1DFBE9} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{295E8539-5450-4764-B3F5-51F968628022} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{C85ED942-8121-453F-8308-9DB730843B63} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{06728BF2-C5EB-44C7-9F30-14FAA5649E14} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{3E4CA7FE-741B-4C78-A775-220E0E3C1B03} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{22BA4EAB-641E-42B2-BB37-9C3BCFD99F76} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{57713B23-CCAB-44DB-A08D-55F9D236D05B} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
{33BB1B86-64BF-45BB-A334-3E1A4802253C} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD305D75-BD1B-43AE-BF04-869DA6A0858F}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,8 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
<Import Project="version.props" />
<Import Project="dependencies.props" />
</Project>

View File

@ -0,0 +1,117 @@
param (
[Parameter(Mandatory = $True)]
[string] $appPoolName
)
# Provisions the HKLM registry so that the specified user account can persist auto-generated machine keys.
function Provision-AutoGenKeys {
[CmdletBinding()]
param (
[ValidateSet("2.0", "4.0")]
[Parameter(Mandatory = $True)]
[string] $frameworkVersion,
[ValidateSet("32", "64")]
[Parameter(Mandatory = $True)]
[string] $architecture,
[Parameter(Mandatory = $True)]
[string] $sid
)
process {
# We require administrative permissions to continue.
if (-Not (new-object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Error "This cmdlet requires Administrator permissions."
return
}
# Open HKLM with an appropriate view into the registry
if ($architecture -eq "32") {
$regView = [Microsoft.Win32.RegistryView]::Registry32;
} else {
$regView = [Microsoft.Win32.RegistryView]::Registry64;
}
$baseRegKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $regView)
# Open ASP.NET base key
if ($frameworkVersion -eq "2.0") {
$expandedVersion = "2.0.50727.0"
} else {
$expandedVersion = "4.0.30319.0"
}
$softwareMicrosoftKey = $baseRegKey.OpenSubKey("SOFTWARE\Microsoft\", $True);
$aspNetKey = $softwareMicrosoftKey.OpenSubKey("ASP.NET", $True);
if ($aspNetKey -eq $null)
{
$aspNetKey = $softwareMicrosoftKey.CreateSubKey("ASP.NET")
}
$aspNetBaseKey = $aspNetKey.OpenSubKey("$expandedVersion", $True);
if ($aspNetBaseKey -eq $null)
{
$aspNetBaseKey = $aspNetKey.CreateSubKey("$expandedVersion")
}
# Create AutoGenKeys subkey if it doesn't already exist
$autoGenBaseKey = $aspNetBaseKey.OpenSubKey("AutoGenKeys", $True)
if ($autoGenBaseKey -eq $null) {
$autoGenBaseKey = $aspNetBaseKey.CreateSubKey("AutoGenKeys")
}
# SYSTEM, ADMINISTRATORS, and the target SID get full access
$regSec = New-Object System.Security.AccessControl.RegistrySecurity
$regSec.SetSecurityDescriptorSddlForm("D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;$sid)")
$userAutoGenKey = $autoGenBaseKey.OpenSubKey($sid, $True)
if ($userAutoGenKey -eq $null) {
# Subkey didn't exist; create and ACL appropriately
$userAutoGenKey = $autoGenBaseKey.CreateSubKey($sid, [Microsoft.Win32.RegistryKeyPermissionCheck]::Default, $regSec)
} else {
# Subkey existed; make sure ACLs are correct
$userAutoGenKey.SetAccessControl($regSec)
}
}
}
$ErrorActionPreference = "Stop"
if (Get-Command Get-IISAppPool -errorAction SilentlyContinue)
{
$processModel = (Get-IISAppPool $appPoolName).processModel
}
else
{
Import-Module WebAdministration
$processModel = Get-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name "processModel"
}
$identityType = $processModel.identityType
Write-Output "Pool process model: '$identityType'"
Switch ($identityType)
{
"LocalService" {
$userName = "LocalService";
}
"LocalSystem" {
$userName = "System";
}
"NetworkService" {
$userName = "NetworkService";
}
"ApplicationPoolIdentity" {
$userName = "IIS APPPOOL\$appPoolName";
}
"SpecificUser" {
$userName = $processModel.userName;
}
}
Write-Output "Pool user name: '$userName'"
Try
{
$poolSid = (New-Object System.Security.Principal.NTAccount($userName)).Translate([System.Security.Principal.SecurityIdentifier]).Value
}
Catch [System.Security.Principal.IdentityNotMappedException]
{
Write-Error "Application pool '$appPoolName' account cannot be resolved."
}
Write-Output "Pool SID: '$poolSid'"
Provision-AutoGenKeys "4.0" "32" $poolSid
Provision-AutoGenKeys "4.0" "64" $poolSid

View File

@ -0,0 +1,8 @@
DataProtection
==============
Data Protection APIs for protecting and unprotecting data. You can find documentation for Data Protection in the [ASP.NET Core Documentation](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/).
## Community Maintained Data Protection Providers & Projects
- [ASP.NET Core DataProtection for Service Fabric](https://github.com/MedAnd/AspNetCore.DataProtection.ServiceFabric)

View File

@ -0,0 +1,3 @@
@ECHO OFF
SET RepoRoot="%~dp0..\.."
%RepoRoot%\build.cmd -LockFile %RepoRoot%\korebuild-lock.txt -Path %~dp0 %*

7
src/DataProtection/build.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
repo_root="$DIR/../.."
"$repo_root/build.sh" --path "$DIR" --lockfile "$repo_root/korebuild-lock.txt" "$@"

View File

@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<!-- TODO: temporary while we reorganize source code and refactor dependency management -->
<DisablePackageReferenceRestrictions>true</DisablePackageReferenceRestrictions>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,17 @@
<Project>
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<PropertyGroup>
<!-- Fallback when not building from command line. -->
<InternalAspNetCoreSdkPackageVersion Condition="'$(InternalAspNetCoreSdkPackageVersion)' == ''">2.2.0-preview2-20181004.6</InternalAspNetCoreSdkPackageVersion>
</PropertyGroup>
<!--
The versions are present to maintain compatibility with the 2.2.0 release of DataProtection,
even though source code and infrastructure has changed.
-->
<PropertyGroup Label="Package Versions: Overrides">
</PropertyGroup>
</Project>

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.AzureStorage\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,43 @@
// 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 Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace AzureBlob
{
public class Program
{
public static void Main(string[] args)
{
var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
var client = storageAccount.CreateCloudBlobClient();
var container = client.GetContainerReference("key-container");
// The container must exist before calling the DataProtection APIs.
// The specific file within the container does not have to exist,
// as it will be created on-demand.
container.CreateIfNotExistsAsync().GetAwaiter().GetResult();
// Configure
using (var services = new ServiceCollection()
.AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug))
.AddDataProtection()
.PersistKeysToAzureBlobStorage(container, "keys.xml")
.Services
.BuildServiceProvider())
{
// Run a sample payload
var protector = services.GetDataProtector("sample-purpose");
var protectedData = protector.Protect("Hello world!");
Console.WriteLine(protectedData);
}
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.AzureKeyVault\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,44 @@
// 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.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile("settings.json");
var config = builder.Build();
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Find(X509FindType.FindByThumbprint, config["CertificateThumbprint"], false);
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging();
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("."))
.ProtectKeysWithAzureKeyVault(config["KeyId"], config["ClientId"], cert.OfType<X509Certificate2>().Single());
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
loggerFactory.AddConsole();
var protector = serviceProvider.GetDataProtector("Test");
Console.WriteLine(protector.Protect("Hello world"));
}
}
}

View File

@ -0,0 +1,5 @@
{
"CertificateThumbprint": "",
"KeyId": "",
"ClientId": ""
}

View File

@ -0,0 +1,31 @@
// 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 Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace CustomEncryptorSample
{
public static class CustomBuilderExtensions
{
public static IDataProtectionBuilder UseXmlEncryptor(
this IDataProtectionBuilder builder,
Func<IServiceProvider, IXmlEncryptor> factory)
{
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(serviceProvider =>
{
var instance = factory(serviceProvider);
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.XmlEncryptor = instance;
});
});
return builder;
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,32 @@
// 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.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CustomEncryptorSample
{
public class CustomXmlDecryptor : IXmlDecryptor
{
private readonly ILogger _logger;
public CustomXmlDecryptor(IServiceProvider services)
{
_logger = services.GetRequiredService<ILoggerFactory>().CreateLogger<CustomXmlDecryptor>();
}
public XElement Decrypt(XElement encryptedElement)
{
if (encryptedElement == null)
{
throw new ArgumentNullException(nameof(encryptedElement));
}
return new XElement(encryptedElement.Elements().Single());
}
}
}

View File

@ -0,0 +1,38 @@
// 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.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CustomEncryptorSample
{
public class CustomXmlEncryptor : IXmlEncryptor
{
private readonly ILogger _logger;
public CustomXmlEncryptor(IServiceProvider services)
{
_logger = services.GetRequiredService<ILoggerFactory>().CreateLogger<CustomXmlEncryptor>();
}
public EncryptedXmlInfo Encrypt(XElement plaintextElement)
{
if (plaintextElement == null)
{
throw new ArgumentNullException(nameof(plaintextElement));
}
_logger.LogInformation("Not encrypting key");
var newElement = new XElement("unencryptedKey",
new XComment(" This key is not encrypted. "),
new XElement(plaintextElement));
var encryptedTextElement = new EncryptedXmlInfo(newElement, typeof(CustomXmlDecryptor));
return encryptedTextElement;
}
}
}

View File

@ -0,0 +1,36 @@
// 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.IO;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CustomEncryptorSample
{
public class Program
{
public static void Main(string[] args)
{
var keysFolder = Path.Combine(Directory.GetCurrentDirectory(), "temp-keys");
using (var services = new ServiceCollection()
.AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug))
.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(keysFolder))
.UseXmlEncryptor(s => new CustomXmlEncryptor(s))
.Services.BuildServiceProvider())
{
var protector = services.GetDataProtector("SamplePurpose");
// protect the payload
var protectedPayload = protector.Protect("Hello World!");
Console.WriteLine($"Protect returned: {protectedPayload}");
// unprotect the payload
var unprotectedPayload = protector.Unprotect(protectedPayload);
Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
}
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
<TargetFrameworks>net461;netcoreapp2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,48 @@
// 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 Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace EntityFrameworkCoreSample
{
class Program
{
static void Main(string[] args)
{
// Configure
var services = new ServiceCollection()
.AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug))
.AddDbContext<DataProtectionKeyContext>(o =>
{
o.UseInMemoryDatabase("DataProtection_EntityFrameworkCore");
o.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
o.EnableSensitiveDataLogging();
})
.AddDataProtection()
.PersistKeysToDbContext<DataProtectionKeyContext>()
.SetDefaultKeyLifetime(TimeSpan.FromDays(7))
.Services
.BuildServiceProvider(validateScopes: true);
using(services)
{
// Run a sample payload
var protector = services.GetDataProtector("sample-purpose");
var protectedData = protector.Protect("Hello world!");
Console.WriteLine(protectedData);
}
}
}
class DataProtectionKeyContext : DbContext, IDataProtectionKeyContext
{
public DataProtectionKeyContext(DbContextOptions<DataProtectionKeyContext> options) : base(options) { }
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,66 @@
// 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.IO;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
namespace KeyManagementSample
{
public class Program
{
public static void Main(string[] args)
{
var keysFolder = Path.Combine(Directory.GetCurrentDirectory(), "temp-keys");
var serviceCollection = new ServiceCollection();
var builder = serviceCollection
.AddDataProtection()
// point at a specific folder and use DPAPI to encrypt keys
.PersistKeysToFileSystem(new DirectoryInfo(keysFolder));
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
builder.ProtectKeysWithDpapi();
}
using (var services = serviceCollection.BuildServiceProvider())
{
// perform a protect operation to force the system to put at least
// one key in the key ring
services.GetDataProtector("Sample.KeyManager.v1").Protect("payload");
Console.WriteLine("Performed a protect operation.");
// get a reference to the key manager
var keyManager = services.GetService<IKeyManager>();
// list all keys in the key ring
var allKeys = keyManager.GetAllKeys();
Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
foreach (var key in allKeys)
{
Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
}
// revoke all keys in the key ring
keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here.");
Console.WriteLine("Revoked all existing keys.");
// add a new key to the key ring with immediate activation and a 1-month expiration
keyManager.CreateNewKey(
activationDate: DateTimeOffset.Now,
expirationDate: DateTimeOffset.Now.AddMonths(1));
Console.WriteLine("Added a new key.");
// list all keys in the key ring
allKeys = keyManager.GetAllKeys();
Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
foreach (var key in allKeys)
{
Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
}
}
}
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,41 @@
// 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.IO;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
namespace NonDISample
{
public class Program
{
public static void Main(string[] args)
{
var keysFolder = Path.Combine(Directory.GetCurrentDirectory(), "temp-keys");
// instantiate the data protection system at this folder
var dataProtectionProvider = DataProtectionProvider.Create(
new DirectoryInfo(keysFolder),
configuration =>
{
configuration.SetApplicationName("my app name");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
configuration.ProtectKeysWithDpapi();
}
});
var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
// protect the payload
var protectedPayload = protector.Protect("Hello World!");
Console.WriteLine($"Protect returned: {protectedPayload}");
// unprotect the payload
var unprotectedPayload = protector.Unprotect(protectedPayload);
Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
}
}
}

View File

@ -0,0 +1,35 @@
// 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 Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.StackExchangeRedis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
namespace RedisSample
{
public class Program
{
public static void Main(string[] args)
{
// Connect
var redis = ConnectionMultiplexer.Connect("localhost:6379");
// Configure
using (var services = new ServiceCollection()
.AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug))
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys")
.Services
.BuildServiceProvider())
{
// Run a sample payload
var protector = services.GetDataProtector("sample-purpose");
var protectedData = protector.Protect("Hello world!");
Console.WriteLine(protectedData);
}
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
<OutputType>exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,14 @@
// 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.Text;
namespace Microsoft.AspNetCore.DataProtection
{
internal static class EncodingUtil
{
// UTF8 encoding that fails on invalid chars
public static readonly UTF8Encoding SecureUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
}
}

View File

@ -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.
using System;
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.DataProtection
{
internal static class ExceptionExtensions
{
/// <summary>
/// Determines whether an exception must be homogenized by being wrapped inside a
/// CryptographicException before being rethrown.
/// </summary>
public static bool RequiresHomogenization(this Exception ex)
{
return !(ex is CryptographicException);
}
}
}

View File

@ -0,0 +1,7 @@
<Project>
<Import Project="..\Directory.Build.props" />
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,38 @@
// 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.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/cc562981(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
{
public uint cbSize;
public uint dwInfoVersion;
public byte* pbNonce;
public uint cbNonce;
public byte* pbAuthData;
public uint cbAuthData;
public byte* pbTag;
public uint cbTag;
public byte* pbMacContext;
public uint cbMacContext;
public uint cbAAD;
public ulong cbData;
public uint dwFlags;
// corresponds to the BCRYPT_INIT_AUTH_MODE_INFO macro in bcrypt.h
public static void Init(out BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO info)
{
const uint BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = 1;
info = new BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
{
cbSize = (uint)sizeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO),
dwInfoVersion = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION
};
}
}
}

View File

@ -0,0 +1,46 @@
// 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.Runtime.InteropServices;
using Microsoft.AspNetCore.Cryptography.Internal;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375525(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal struct BCRYPT_KEY_LENGTHS_STRUCT
{
// MSDN says these fields represent the key length in bytes.
// It's wrong: these key lengths are all actually in bits.
internal uint dwMinLength;
internal uint dwMaxLength;
internal uint dwIncrement;
public void EnsureValidKeyLength(uint keyLengthInBits)
{
if (!IsValidKeyLength(keyLengthInBits))
{
string message = Resources.FormatBCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength(keyLengthInBits, dwMinLength, dwMaxLength, dwIncrement);
throw new ArgumentOutOfRangeException(nameof(keyLengthInBits), message);
}
CryptoUtil.Assert(keyLengthInBits % 8 == 0, "keyLengthInBits % 8 == 0");
}
private bool IsValidKeyLength(uint keyLengthInBits)
{
// If the step size is zero, then the key length must be exactly the min or the max. Otherwise,
// key length must be between min and max (inclusive) and a whole number of increments away from min.
if (dwIncrement == 0)
{
return (keyLengthInBits == dwMinLength || keyLengthInBits == dwMaxLength);
}
else
{
return (dwMinLength <= keyLengthInBits)
&& (keyLengthInBits <= dwMaxLength)
&& ((keyLengthInBits - dwMinLength) % dwIncrement == 0);
}
}
}
}

View File

@ -0,0 +1,17 @@
// 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.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375368(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal struct BCryptBuffer
{
public uint cbBuffer; // Length of buffer, in bytes
public BCryptKeyDerivationBufferType BufferType; // Buffer type
public IntPtr pvBuffer; // Pointer to buffer
}
}

View File

@ -0,0 +1,26 @@
// 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375370(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct BCryptBufferDesc
{
private const int BCRYPTBUFFER_VERSION = 0;
public uint ulVersion; // Version number
public uint cBuffers; // Number of buffers
public BCryptBuffer* pBuffers; // Pointer to array of buffers
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Initialize(ref BCryptBufferDesc bufferDesc)
{
bufferDesc.ulVersion = BCRYPTBUFFER_VERSION;
}
}
}

View File

@ -0,0 +1,13 @@
// 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;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
[Flags]
internal enum BCryptEncryptFlags
{
BCRYPT_BLOCK_PADDING = 0x00000001,
}
}

View File

@ -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;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
// from bcrypt.h
[Flags]
internal enum BCryptGenRandomFlags
{
BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001,
BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002,
}
}

View File

@ -0,0 +1,29 @@
// 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;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
// from bcrypt.h
internal enum BCryptKeyDerivationBufferType
{
KDF_HASH_ALGORITHM = 0x0,
KDF_SECRET_PREPEND = 0x1,
KDF_SECRET_APPEND = 0x2,
KDF_HMAC_KEY = 0x3,
KDF_TLS_PRF_LABEL = 0x4,
KDF_TLS_PRF_SEED = 0x5,
KDF_SECRET_HANDLE = 0x6,
KDF_TLS_PRF_PROTOCOL = 0x7,
KDF_ALGORITHMID = 0x8,
KDF_PARTYUINFO = 0x9,
KDF_PARTYVINFO = 0xA,
KDF_SUPPPUBINFO = 0xB,
KDF_SUPPPRIVINFO = 0xC,
KDF_LABEL = 0xD,
KDF_CONTEXT = 0xE,
KDF_SALT = 0xF,
KDF_ITERATION_COUNT = 0x10,
}
}

View File

@ -0,0 +1,29 @@
// 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;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
/// <summary>
/// Wraps utility BCRYPT APIs that don't work directly with handles.
/// </summary>
internal unsafe static class BCryptUtil
{
/// <summary>
/// Fills a buffer with cryptographically secure random data.
/// </summary>
public static void GenRandom(byte* pbBuffer, uint cbBuffer)
{
if (cbBuffer != 0)
{
int ntstatus = UnsafeNativeMethods.BCryptGenRandom(
hAlgorithm: IntPtr.Zero,
pbBuffer: pbBuffer,
cbBuffer: cbBuffer,
dwFlags: BCryptGenRandomFlags.BCRYPT_USE_SYSTEM_PREFERRED_RNG);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
}
}
}
}

View File

@ -0,0 +1,93 @@
// 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 Microsoft.AspNetCore.Cryptography.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
/// <summary>
/// Provides cached CNG algorithm provider instances, as calling BCryptOpenAlgorithmProvider is expensive.
/// Callers should use caution never to dispose of the algorithm provider instances returned by this type.
/// </summary>
internal static class CachedAlgorithmHandles
{
private static CachedAlgorithmInfo _aesCbc = new CachedAlgorithmInfo(() => GetAesAlgorithm(chainingMode: Constants.BCRYPT_CHAIN_MODE_CBC));
private static CachedAlgorithmInfo _aesGcm = new CachedAlgorithmInfo(() => GetAesAlgorithm(chainingMode: Constants.BCRYPT_CHAIN_MODE_GCM));
private static CachedAlgorithmInfo _hmacSha1 = new CachedAlgorithmInfo(() => GetHmacAlgorithm(algorithm: Constants.BCRYPT_SHA1_ALGORITHM));
private static CachedAlgorithmInfo _hmacSha256 = new CachedAlgorithmInfo(() => GetHmacAlgorithm(algorithm: Constants.BCRYPT_SHA256_ALGORITHM));
private static CachedAlgorithmInfo _hmacSha512 = new CachedAlgorithmInfo(() => GetHmacAlgorithm(algorithm: Constants.BCRYPT_SHA512_ALGORITHM));
private static CachedAlgorithmInfo _pbkdf2 = new CachedAlgorithmInfo(GetPbkdf2Algorithm);
private static CachedAlgorithmInfo _sha1 = new CachedAlgorithmInfo(() => GetHashAlgorithm(algorithm: Constants.BCRYPT_SHA1_ALGORITHM));
private static CachedAlgorithmInfo _sha256 = new CachedAlgorithmInfo(() => GetHashAlgorithm(algorithm: Constants.BCRYPT_SHA256_ALGORITHM));
private static CachedAlgorithmInfo _sha512 = new CachedAlgorithmInfo(() => GetHashAlgorithm(algorithm: Constants.BCRYPT_SHA512_ALGORITHM));
private static CachedAlgorithmInfo _sp800_108_ctr_hmac = new CachedAlgorithmInfo(GetSP800_108_CTR_HMACAlgorithm);
public static BCryptAlgorithmHandle AES_CBC => CachedAlgorithmInfo.GetAlgorithmHandle(ref _aesCbc);
public static BCryptAlgorithmHandle AES_GCM => CachedAlgorithmInfo.GetAlgorithmHandle(ref _aesGcm);
public static BCryptAlgorithmHandle HMAC_SHA1 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha1);
public static BCryptAlgorithmHandle HMAC_SHA256 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha256);
public static BCryptAlgorithmHandle HMAC_SHA512 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha512);
// Only available on Win8+.
public static BCryptAlgorithmHandle PBKDF2 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _pbkdf2);
public static BCryptAlgorithmHandle SHA1 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha1);
public static BCryptAlgorithmHandle SHA256 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha256);
public static BCryptAlgorithmHandle SHA512 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha512);
// Only available on Win8+.
public static BCryptAlgorithmHandle SP800_108_CTR_HMAC => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sp800_108_ctr_hmac);
private static BCryptAlgorithmHandle GetAesAlgorithm(string chainingMode)
{
var algHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(Constants.BCRYPT_AES_ALGORITHM);
algHandle.SetChainingMode(chainingMode);
return algHandle;
}
private static BCryptAlgorithmHandle GetHashAlgorithm(string algorithm)
{
return BCryptAlgorithmHandle.OpenAlgorithmHandle(algorithm, hmac: false);
}
private static BCryptAlgorithmHandle GetHmacAlgorithm(string algorithm)
{
return BCryptAlgorithmHandle.OpenAlgorithmHandle(algorithm, hmac: true);
}
private static BCryptAlgorithmHandle GetPbkdf2Algorithm()
{
return BCryptAlgorithmHandle.OpenAlgorithmHandle(Constants.BCRYPT_PBKDF2_ALGORITHM, implementation: Constants.MS_PRIMITIVE_PROVIDER);
}
private static BCryptAlgorithmHandle GetSP800_108_CTR_HMACAlgorithm()
{
return BCryptAlgorithmHandle.OpenAlgorithmHandle(Constants.BCRYPT_SP800108_CTR_HMAC_ALGORITHM, implementation: Constants.MS_PRIMITIVE_PROVIDER);
}
// Warning: mutable struct!
private struct CachedAlgorithmInfo
{
private WeakReference<BCryptAlgorithmHandle> _algorithmHandle;
private readonly Func<BCryptAlgorithmHandle> _factory;
public CachedAlgorithmInfo(Func<BCryptAlgorithmHandle> factory)
{
_algorithmHandle = null;
_factory = factory;
}
public static BCryptAlgorithmHandle GetAlgorithmHandle(ref CachedAlgorithmInfo cachedAlgorithmInfo)
{
return WeakReferenceHelpers.GetSharedInstance(ref cachedAlgorithmInfo._algorithmHandle, cachedAlgorithmInfo._factory);
}
}
}
}

View File

@ -0,0 +1,17 @@
// 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;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
[Flags]
internal enum NCryptEncryptFlags
{
NCRYPT_NO_PADDING_FLAG = 0x00000001,
NCRYPT_PAD_PKCS1_FLAG = 0x00000002,
NCRYPT_PAD_OAEP_FLAG = 0x00000004,
NCRYPT_PAD_PSS_FLAG = 0x00000008,
NCRYPT_SILENT_FLAG = 0x00000040,
}
}

View File

@ -0,0 +1,66 @@
// 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 Microsoft.AspNetCore.Cryptography.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.Cng
{
internal static class OSVersionUtil
{
private static readonly OSVersion _osVersion = GetOSVersion();
private static OSVersion GetOSVersion()
{
const string BCRYPT_LIB = "bcrypt.dll";
SafeLibraryHandle bcryptLibHandle = null;
try
{
bcryptLibHandle = SafeLibraryHandle.Open(BCRYPT_LIB);
}
catch
{
// we'll handle the exceptional case later
}
if (bcryptLibHandle != null)
{
using (bcryptLibHandle)
{
if (bcryptLibHandle.DoesProcExist("BCryptKeyDerivation"))
{
// We're running on Win8+.
return OSVersion.Win8OrLater;
}
else
{
// We're running on Win7+.
return OSVersion.Win7OrLater;
}
}
}
else
{
// Not running on Win7+.
return OSVersion.NotWindows;
}
}
public static bool IsWindows()
{
return (_osVersion >= OSVersion.Win7OrLater);
}
public static bool IsWindows8OrLater()
{
return (_osVersion >= OSVersion.Win8OrLater);
}
private enum OSVersion
{
NotWindows = 0,
Win7OrLater = 1,
Win8OrLater = 2
}
}
}

View File

@ -0,0 +1,88 @@
// 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;
namespace Microsoft.AspNetCore.Cryptography
{
// The majority of these are from bcrypt.h
internal static class Constants
{
internal const int MAX_STACKALLOC_BYTES = 256; // greatest number of bytes that we'll ever allow to stackalloc in a single frame
// BCrypt(Import/Export)Key BLOB types
internal const string BCRYPT_OPAQUE_KEY_BLOB = "OpaqueKeyBlob";
internal const string BCRYPT_KEY_DATA_BLOB = "KeyDataBlob";
internal const string BCRYPT_AES_WRAP_KEY_BLOB = "Rfc3565KeyWrapBlob";
// Microsoft built-in providers
internal const string MS_PRIMITIVE_PROVIDER = "Microsoft Primitive Provider";
internal const string MS_PLATFORM_CRYPTO_PROVIDER = "Microsoft Platform Crypto Provider";
// Common algorithm identifiers
internal const string BCRYPT_RSA_ALGORITHM = "RSA";
internal const string BCRYPT_RSA_SIGN_ALGORITHM = "RSA_SIGN";
internal const string BCRYPT_DH_ALGORITHM = "DH";
internal const string BCRYPT_DSA_ALGORITHM = "DSA";
internal const string BCRYPT_RC2_ALGORITHM = "RC2";
internal const string BCRYPT_RC4_ALGORITHM = "RC4";
internal const string BCRYPT_AES_ALGORITHM = "AES";
internal const string BCRYPT_DES_ALGORITHM = "DES";
internal const string BCRYPT_DESX_ALGORITHM = "DESX";
internal const string BCRYPT_3DES_ALGORITHM = "3DES";
internal const string BCRYPT_3DES_112_ALGORITHM = "3DES_112";
internal const string BCRYPT_MD2_ALGORITHM = "MD2";
internal const string BCRYPT_MD4_ALGORITHM = "MD4";
internal const string BCRYPT_MD5_ALGORITHM = "MD5";
internal const string BCRYPT_SHA1_ALGORITHM = "SHA1";
internal const string BCRYPT_SHA256_ALGORITHM = "SHA256";
internal const string BCRYPT_SHA384_ALGORITHM = "SHA384";
internal const string BCRYPT_SHA512_ALGORITHM = "SHA512";
internal const string BCRYPT_AES_GMAC_ALGORITHM = "AES-GMAC";
internal const string BCRYPT_AES_CMAC_ALGORITHM = "AES-CMAC";
internal const string BCRYPT_ECDSA_P256_ALGORITHM = "ECDSA_P256";
internal const string BCRYPT_ECDSA_P384_ALGORITHM = "ECDSA_P384";
internal const string BCRYPT_ECDSA_P521_ALGORITHM = "ECDSA_P521";
internal const string BCRYPT_ECDH_P256_ALGORITHM = "ECDH_P256";
internal const string BCRYPT_ECDH_P384_ALGORITHM = "ECDH_P384";
internal const string BCRYPT_ECDH_P521_ALGORITHM = "ECDH_P521";
internal const string BCRYPT_RNG_ALGORITHM = "RNG";
internal const string BCRYPT_RNG_FIPS186_DSA_ALGORITHM = "FIPS186DSARNG";
internal const string BCRYPT_RNG_DUAL_EC_ALGORITHM = "DUALECRNG";
internal const string BCRYPT_SP800108_CTR_HMAC_ALGORITHM = "SP800_108_CTR_HMAC";
internal const string BCRYPT_SP80056A_CONCAT_ALGORITHM = "SP800_56A_CONCAT";
internal const string BCRYPT_PBKDF2_ALGORITHM = "PBKDF2";
internal const string BCRYPT_CAPI_KDF_ALGORITHM = "CAPI_KDF";
// BCryptGetProperty strings
internal const string BCRYPT_OBJECT_LENGTH = "ObjectLength";
internal const string BCRYPT_ALGORITHM_NAME = "AlgorithmName";
internal const string BCRYPT_PROVIDER_HANDLE = "ProviderHandle";
internal const string BCRYPT_CHAINING_MODE = "ChainingMode";
internal const string BCRYPT_BLOCK_LENGTH = "BlockLength";
internal const string BCRYPT_KEY_LENGTH = "KeyLength";
internal const string BCRYPT_KEY_OBJECT_LENGTH = "KeyObjectLength";
internal const string BCRYPT_KEY_STRENGTH = "KeyStrength";
internal const string BCRYPT_KEY_LENGTHS = "KeyLengths";
internal const string BCRYPT_BLOCK_SIZE_LIST = "BlockSizeList";
internal const string BCRYPT_EFFECTIVE_KEY_LENGTH = "EffectiveKeyLength";
internal const string BCRYPT_HASH_LENGTH = "HashDigestLength";
internal const string BCRYPT_HASH_OID_LIST = "HashOIDList";
internal const string BCRYPT_PADDING_SCHEMES = "PaddingSchemes";
internal const string BCRYPT_SIGNATURE_LENGTH = "SignatureLength";
internal const string BCRYPT_HASH_BLOCK_LENGTH = "HashBlockLength";
internal const string BCRYPT_AUTH_TAG_LENGTH = "AuthTagLength";
internal const string BCRYPT_PRIMITIVE_TYPE = "PrimitiveType";
internal const string BCRYPT_IS_KEYED_HASH = "IsKeyedHash";
internal const string BCRYPT_IS_REUSABLE_HASH = "IsReusableHash";
internal const string BCRYPT_MESSAGE_BLOCK_LENGTH = "MessageBlockLength";
// Property Strings
internal const string BCRYPT_CHAIN_MODE_NA = "ChainingModeN/A";
internal const string BCRYPT_CHAIN_MODE_CBC = "ChainingModeCBC";
internal const string BCRYPT_CHAIN_MODE_ECB = "ChainingModeECB";
internal const string BCRYPT_CHAIN_MODE_CFB = "ChainingModeCFB";
internal const string BCRYPT_CHAIN_MODE_CCM = "ChainingModeCCM";
internal const string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM";
}
}

View File

@ -0,0 +1,99 @@
// 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.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.Internal;
namespace Microsoft.AspNetCore.Cryptography
{
internal unsafe static class CryptoUtil
{
// This isn't a typical Debug.Assert; the check is always performed, even in retail builds.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Assert(bool condition, string message)
{
if (!condition)
{
Fail(message);
}
}
// This isn't a typical Debug.Assert; the check is always performed, even in retail builds.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertSafeHandleIsValid(SafeHandle safeHandle)
{
Assert(safeHandle != null && !safeHandle.IsInvalid, "Safe handle is invalid.");
}
// Asserts that the current platform is Windows; throws PlatformNotSupportedException otherwise.
public static void AssertPlatformIsWindows()
{
if (!OSVersionUtil.IsWindows())
{
throw new PlatformNotSupportedException(Resources.Platform_Windows7Required);
}
}
// Asserts that the current platform is Windows 8 or above; throws PlatformNotSupportedException otherwise.
public static void AssertPlatformIsWindows8OrLater()
{
if (!OSVersionUtil.IsWindows8OrLater())
{
throw new PlatformNotSupportedException(Resources.Platform_Windows8Required);
}
}
// This isn't a typical Debug.Fail; an error always occurs, even in retail builds.
// This method doesn't return, but since the CLR doesn't allow specifying a 'never'
// return type, we mimic it by specifying our return type as Exception. That way
// callers can write 'throw Fail(...);' to make the C# compiler happy, as the
// throw keyword is implicitly of type O.
[MethodImpl(MethodImplOptions.NoInlining)]
public static Exception Fail(string message)
{
Debug.Fail(message);
throw new CryptographicException("Assertion failed: " + message);
}
// Allows callers to write "var x = Method() ?? Fail<T>(message);" as a convenience to guard
// against a method returning null unexpectedly.
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Fail<T>(string message) where T : class
{
throw Fail(message);
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint count)
{
bool areEqual = true;
for (uint i = 0; i < count; i++)
{
areEqual &= (bufA[i] == bufB[i]);
}
return areEqual;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool TimeConstantBuffersAreEqual(byte[] bufA, int offsetA, int countA, byte[] bufB, int offsetB, int countB)
{
// Technically this is an early exit scenario, but it means that the caller did something bizarre.
// An error at the call site isn't usable for timing attacks.
Assert(countA == countB, "countA == countB");
bool areEqual = true;
for (int i = 0; i < countA; i++)
{
areEqual &= (bufA[offsetA + i] == bufB[offsetB + i]);
}
return areEqual;
}
}
}

View File

@ -0,0 +1,16 @@
// 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.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Cryptography
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa381414(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DATA_BLOB
{
public uint cbData;
public byte* pbData;
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Infrastructure for ASP.NET Core cryptographic packages. Applications and libraries should not reference this package directly.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,16 @@
// 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;
using System.Runtime.InteropServices;
// we only ever p/invoke into DLLs known to be in the System32 folder
[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Cryptography.Internal.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Cryptography.KeyDerivation, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Cryptography.KeyDerivation.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection.Abstractions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,86 @@
// <auto-generated />
namespace Microsoft.AspNetCore.Cryptography.Internal
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNetCore.Cryptography.Internal.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// A provider could not be found for algorithm '{0}'.
/// </summary>
internal static string BCryptAlgorithmHandle_ProviderNotFound
{
get => GetString("BCryptAlgorithmHandle_ProviderNotFound");
}
/// <summary>
/// A provider could not be found for algorithm '{0}'.
/// </summary>
internal static string FormatBCryptAlgorithmHandle_ProviderNotFound(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("BCryptAlgorithmHandle_ProviderNotFound"), p0);
/// <summary>
/// The key length {0} is invalid. Valid key lengths are {1} to {2} bits (step size {3}).
/// </summary>
internal static string BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength
{
get => GetString("BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength");
}
/// <summary>
/// The key length {0} is invalid. Valid key lengths are {1} to {2} bits (step size {3}).
/// </summary>
internal static string FormatBCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength(object p0, object p1, object p2, object p3)
=> string.Format(CultureInfo.CurrentCulture, GetString("BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength"), p0, p1, p2, p3);
/// <summary>
/// This operation requires Windows 7 / Windows Server 2008 R2 or later.
/// </summary>
internal static string Platform_Windows7Required
{
get => GetString("Platform_Windows7Required");
}
/// <summary>
/// This operation requires Windows 7 / Windows Server 2008 R2 or later.
/// </summary>
internal static string FormatPlatform_Windows7Required()
=> GetString("Platform_Windows7Required");
/// <summary>
/// This operation requires Windows 8 / Windows Server 2012 or later.
/// </summary>
internal static string Platform_Windows8Required
{
get => GetString("Platform_Windows8Required");
}
/// <summary>
/// This operation requires Windows 8 / Windows Server 2012 or later.
/// </summary>
internal static string FormatPlatform_Windows8Required()
=> GetString("Platform_Windows8Required");
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BCryptAlgorithmHandle_ProviderNotFound" xml:space="preserve">
<value>A provider could not be found for algorithm '{0}'.</value>
</data>
<data name="BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength" xml:space="preserve">
<value>The key length {0} is invalid. Valid key lengths are {1} to {2} bits (step size {3}).</value>
</data>
<data name="Platform_Windows7Required" xml:space="preserve">
<value>This operation requires Windows 7 / Windows Server 2008 R2 or later.</value>
</data>
<data name="Platform_Windows8Required" xml:space="preserve">
<value>This operation requires Windows 8 / Windows Server 2012 or later.</value>
</data>
</root>

View File

@ -0,0 +1,170 @@
// 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.Diagnostics;
using System.Globalization;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.Internal;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
/// <summary>
/// Represents a handle to a BCrypt algorithm provider from which keys and hashes can be created.
/// </summary>
internal unsafe sealed class BCryptAlgorithmHandle : BCryptHandle
{
// Called by P/Invoke when returning SafeHandles
private BCryptAlgorithmHandle() { }
/// <summary>
/// Creates an unkeyed hash handle from this hash algorithm.
/// </summary>
public BCryptHashHandle CreateHash()
{
return CreateHashCore(null, 0);
}
private BCryptHashHandle CreateHashCore(byte* pbKey, uint cbKey)
{
BCryptHashHandle retVal;
int ntstatus = UnsafeNativeMethods.BCryptCreateHash(this, out retVal, IntPtr.Zero, 0, pbKey, cbKey, dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
CryptoUtil.AssertSafeHandleIsValid(retVal);
retVal.SetAlgorithmProviderHandle(this);
return retVal;
}
/// <summary>
/// Creates an HMAC hash handle from this hash algorithm.
/// </summary>
public BCryptHashHandle CreateHmac(byte* pbKey, uint cbKey)
{
Debug.Assert(pbKey != null);
return CreateHashCore(pbKey, cbKey);
}
/// <summary>
/// Imports a key into a symmetric encryption or KDF algorithm.
/// </summary>
public BCryptKeyHandle GenerateSymmetricKey(byte* pbSecret, uint cbSecret)
{
BCryptKeyHandle retVal;
int ntstatus = UnsafeNativeMethods.BCryptGenerateSymmetricKey(this, out retVal, IntPtr.Zero, 0, pbSecret, cbSecret, 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
CryptoUtil.AssertSafeHandleIsValid(retVal);
retVal.SetAlgorithmProviderHandle(this);
return retVal;
}
/// <summary>
/// Gets the name of this BCrypt algorithm.
/// </summary>
public string GetAlgorithmName()
{
// First, calculate how many characters are in the name.
uint byteLengthOfNameWithTerminatingNull = GetProperty(Constants.BCRYPT_ALGORITHM_NAME, null, 0);
CryptoUtil.Assert(byteLengthOfNameWithTerminatingNull % sizeof(char) == 0 && byteLengthOfNameWithTerminatingNull > sizeof(char), "byteLengthOfNameWithTerminatingNull % sizeof(char) == 0 && byteLengthOfNameWithTerminatingNull > sizeof(char)");
uint numCharsWithoutNull = (byteLengthOfNameWithTerminatingNull - 1) / sizeof(char);
if (numCharsWithoutNull == 0)
{
return String.Empty; // degenerate case
}
// Allocate a string object and write directly into it (CLR team approves of this mechanism).
string retVal = new String((char)0, checked((int)numCharsWithoutNull));
uint numBytesCopied;
fixed (char* pRetVal = retVal)
{
numBytesCopied = GetProperty(Constants.BCRYPT_ALGORITHM_NAME, pRetVal, byteLengthOfNameWithTerminatingNull);
}
CryptoUtil.Assert(numBytesCopied == byteLengthOfNameWithTerminatingNull, "numBytesCopied == byteLengthOfNameWithTerminatingNull");
return retVal;
}
/// <summary>
/// Gets the cipher block length (in bytes) of this block cipher algorithm.
/// </summary>
public uint GetCipherBlockLength()
{
uint cipherBlockLength;
uint numBytesCopied = GetProperty(Constants.BCRYPT_BLOCK_LENGTH, &cipherBlockLength, sizeof(uint));
CryptoUtil.Assert(numBytesCopied == sizeof(uint), "numBytesCopied == sizeof(uint)");
return cipherBlockLength;
}
/// <summary>
/// Gets the hash block length (in bytes) of this hash algorithm.
/// </summary>
public uint GetHashBlockLength()
{
uint hashBlockLength;
uint numBytesCopied = GetProperty(Constants.BCRYPT_HASH_BLOCK_LENGTH, &hashBlockLength, sizeof(uint));
CryptoUtil.Assert(numBytesCopied == sizeof(uint), "numBytesCopied == sizeof(uint)");
return hashBlockLength;
}
/// <summary>
/// Gets the key lengths (in bits) supported by this algorithm.
/// </summary>
public BCRYPT_KEY_LENGTHS_STRUCT GetSupportedKeyLengths()
{
BCRYPT_KEY_LENGTHS_STRUCT supportedKeyLengths;
uint numBytesCopied = GetProperty(Constants.BCRYPT_KEY_LENGTHS, &supportedKeyLengths, (uint)sizeof(BCRYPT_KEY_LENGTHS_STRUCT));
CryptoUtil.Assert(numBytesCopied == sizeof(BCRYPT_KEY_LENGTHS_STRUCT), "numBytesCopied == sizeof(BCRYPT_KEY_LENGTHS_STRUCT)");
return supportedKeyLengths;
}
/// <summary>
/// Gets the digest length (in bytes) of this hash algorithm provider.
/// </summary>
public uint GetHashDigestLength()
{
uint digestLength;
uint numBytesCopied = GetProperty(Constants.BCRYPT_HASH_LENGTH, &digestLength, sizeof(uint));
CryptoUtil.Assert(numBytesCopied == sizeof(uint), "numBytesCopied == sizeof(uint)");
return digestLength;
}
public static BCryptAlgorithmHandle OpenAlgorithmHandle(string algorithmId, string implementation = null, bool hmac = false)
{
// from bcrypt.h
const uint BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008;
// from ntstatus.h
const int STATUS_NOT_FOUND = unchecked((int)0xC0000225);
BCryptAlgorithmHandle algHandle;
int ntstatus = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algHandle, algorithmId, implementation, dwFlags: (hmac) ? BCRYPT_ALG_HANDLE_HMAC_FLAG : 0);
// error checking
if (ntstatus == STATUS_NOT_FOUND)
{
string message = String.Format(CultureInfo.CurrentCulture, Resources.BCryptAlgorithmHandle_ProviderNotFound, algorithmId);
throw new CryptographicException(message);
}
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
CryptoUtil.AssertSafeHandleIsValid(algHandle);
return algHandle;
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
return (UnsafeNativeMethods.BCryptCloseAlgorithmProvider(handle, dwFlags: 0) == 0);
}
public void SetChainingMode(string chainingMode)
{
fixed (char* pszChainingMode = chainingMode ?? String.Empty)
{
SetProperty(Constants.BCRYPT_CHAINING_MODE, pszChainingMode, checked((uint)(chainingMode.Length + 1 /* null terminator */) * sizeof(char)));
}
}
}
}

View File

@ -0,0 +1,30 @@
// 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 Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
internal unsafe abstract class BCryptHandle : SafeHandleZeroOrMinusOneIsInvalid
{
protected BCryptHandle()
: base(ownsHandle: true)
{
}
protected uint GetProperty(string pszProperty, void* pbOutput, uint cbOutput)
{
uint retVal;
int ntstatus = UnsafeNativeMethods.BCryptGetProperty(this, pszProperty, pbOutput, cbOutput, out retVal, dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
return retVal;
}
protected void SetProperty(string pszProperty, void* pbInput, uint cbInput)
{
int ntstatus = UnsafeNativeMethods.BCryptSetProperty(this, pszProperty, pbInput, cbInput, dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
}
}
}

View File

@ -0,0 +1,71 @@
// 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 Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
internal unsafe sealed class BCryptHashHandle : BCryptHandle
{
private BCryptAlgorithmHandle _algProviderHandle;
// Called by P/Invoke when returning SafeHandles
private BCryptHashHandle() { }
/// <summary>
/// Duplicates this hash handle, including any existing hashed state.
/// </summary>
public BCryptHashHandle DuplicateHash()
{
BCryptHashHandle duplicateHandle;
int ntstatus = UnsafeNativeMethods.BCryptDuplicateHash(this, out duplicateHandle, IntPtr.Zero, 0, 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
CryptoUtil.AssertSafeHandleIsValid(duplicateHandle);
duplicateHandle._algProviderHandle = this._algProviderHandle;
return duplicateHandle;
}
/// <summary>
/// Calculates the cryptographic hash over a set of input data.
/// </summary>
public void HashData(byte* pbInput, uint cbInput, byte* pbHashDigest, uint cbHashDigest)
{
int ntstatus;
if (cbInput > 0)
{
ntstatus = UnsafeNativeMethods.BCryptHashData(
hHash: this,
pbInput: pbInput,
cbInput: cbInput,
dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
}
ntstatus = UnsafeNativeMethods.BCryptFinishHash(
hHash: this,
pbOutput: pbHashDigest,
cbOutput: cbHashDigest,
dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
return (UnsafeNativeMethods.BCryptDestroyHash(handle) == 0);
}
// We don't actually need to hold a reference to the algorithm handle, as the native CNG library
// already holds the reference for us. But once we create a hash from an algorithm provider, odds
// are good that we'll create another hash from the same algorithm provider at some point in the
// future. And since algorithm providers are expensive to create, we'll hold a strong reference
// to all known in-use providers. This way the cached algorithm provider handles utility class
// doesn't keep creating providers over and over.
internal void SetAlgorithmProviderHandle(BCryptAlgorithmHandle algProviderHandle)
{
_algProviderHandle = algProviderHandle;
}
}
}

View File

@ -0,0 +1,33 @@
// 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;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
internal sealed class BCryptKeyHandle : BCryptHandle
{
private BCryptAlgorithmHandle _algProviderHandle;
// Called by P/Invoke when returning SafeHandles
private BCryptKeyHandle() { }
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
_algProviderHandle = null;
return (UnsafeNativeMethods.BCryptDestroyKey(handle) == 0);
}
// We don't actually need to hold a reference to the algorithm handle, as the native CNG library
// already holds the reference for us. But once we create a key from an algorithm provider, odds
// are good that we'll create another key from the same algorithm provider at some point in the
// future. And since algorithm providers are expensive to create, we'll hold a strong reference
// to all known in-use providers. This way the cached algorithm provider handles utility class
// doesn't keep creating providers over and over.
internal void SetAlgorithmProviderHandle(BCryptAlgorithmHandle algProviderHandle)
{
_algProviderHandle = algProviderHandle;
}
}
}

View File

@ -0,0 +1,26 @@
// 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.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
/// <summary>
/// Represents a handle returned by LocalAlloc.
/// </summary>
internal class LocalAllocHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Called by P/Invoke when returning SafeHandles
protected LocalAllocHandle()
: base(ownsHandle: true) { }
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle); // actually calls LocalFree
return true;
}
}
}

View File

@ -0,0 +1,42 @@
// 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 Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
internal unsafe sealed class NCryptDescriptorHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private NCryptDescriptorHandle()
: base(ownsHandle: true)
{
}
public string GetProtectionDescriptorRuleString()
{
// from ncryptprotect.h
const int NCRYPT_PROTECTION_INFO_TYPE_DESCRIPTOR_STRING = 0x00000001;
LocalAllocHandle ruleStringHandle;
int ntstatus = UnsafeNativeMethods.NCryptGetProtectionDescriptorInfo(
hDescriptor: this,
pMemPara: IntPtr.Zero,
dwInfoType: NCRYPT_PROTECTION_INFO_TYPE_DESCRIPTOR_STRING,
ppvInfo: out ruleStringHandle);
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
CryptoUtil.AssertSafeHandleIsValid(ruleStringHandle);
using (ruleStringHandle)
{
return new String((char*)ruleStringHandle.DangerousGetHandle());
}
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
return (UnsafeNativeMethods.NCryptCloseProtectionDescriptor(handle) == 0);
}
}
}

View File

@ -0,0 +1,176 @@
// 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.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
/// <summary>
/// Represents a handle to a Windows module (DLL).
/// </summary>
internal unsafe sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Called by P/Invoke when returning SafeHandles
private SafeLibraryHandle()
: base(ownsHandle: true)
{ }
/// <summary>
/// Returns a value stating whether the library exports a given proc.
/// </summary>
public bool DoesProcExist(string lpProcName)
{
IntPtr pfnProc = UnsafeNativeMethods.GetProcAddress(this, lpProcName);
return (pfnProc != IntPtr.Zero);
}
/// <summary>
/// Forbids this library from being unloaded. The library will remain loaded until process termination,
/// regardless of how many times FreeLibrary is called.
/// </summary>
public void ForbidUnload()
{
// from winbase.h
const uint GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004U;
const uint GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001U;
IntPtr unused;
bool retVal = UnsafeNativeMethods.GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, this, out unused);
if (!retVal)
{
UnsafeNativeMethods.ThrowExceptionForLastWin32Error();
}
}
/// <summary>
/// Formats a message string using the resource table in the specified library.
/// </summary>
public string FormatMessage(int messageId)
{
// from winbase.h
const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
LocalAllocHandle messageHandle;
int numCharsOutput = UnsafeNativeMethods.FormatMessage(
dwFlags: FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
lpSource: this,
dwMessageId: (uint)messageId,
dwLanguageId: 0 /* ignore current culture */,
lpBuffer: out messageHandle,
nSize: 0 /* unused */,
Arguments: IntPtr.Zero /* unused */);
if (numCharsOutput != 0 && messageHandle != null && !messageHandle.IsInvalid)
{
// Successfully retrieved the message.
using (messageHandle)
{
return new String((char*)messageHandle.DangerousGetHandle(), 0, numCharsOutput).Trim();
}
}
else
{
// Message not found - that's fine.
return null;
}
}
/// <summary>
/// Gets a delegate pointing to a given export from this library.
/// </summary>
public TDelegate GetProcAddress<TDelegate>(string lpProcName, bool throwIfNotFound = true) where TDelegate : class
{
IntPtr pfnProc = UnsafeNativeMethods.GetProcAddress(this, lpProcName);
if (pfnProc == IntPtr.Zero)
{
if (throwIfNotFound)
{
UnsafeNativeMethods.ThrowExceptionForLastWin32Error();
}
else
{
return null;
}
}
return Marshal.GetDelegateForFunctionPointer<TDelegate>(pfnProc);
}
/// <summary>
/// Opens a library. If 'filename' is not a fully-qualified path, the default search path is used.
/// </summary>
public static SafeLibraryHandle Open(string filename)
{
const uint LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800U; // from libloaderapi.h
SafeLibraryHandle handle = UnsafeNativeMethods.LoadLibraryEx(filename, IntPtr.Zero, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (handle == null || handle.IsInvalid)
{
UnsafeNativeMethods.ThrowExceptionForLastWin32Error();
}
return handle;
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
return UnsafeNativeMethods.FreeLibrary(handle);
}
[SuppressUnmanagedCodeSecurity]
private static class UnsafeNativeMethods
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx
[DllImport("kernel32.dll", EntryPoint = "FormatMessageW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int FormatMessage(
[In] uint dwFlags,
[In] SafeLibraryHandle lpSource,
[In] uint dwMessageId,
[In] uint dwLanguageId,
[Out] out LocalAllocHandle lpBuffer,
[In] uint nSize,
[In] IntPtr Arguments
);
// http://msdn.microsoft.com/en-us/library/ms683152(v=vs.85).aspx
[return: MarshalAs(UnmanagedType.Bool)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern bool FreeLibrary(IntPtr hModule);
// http://msdn.microsoft.com/en-us/library/ms683200(v=vs.85).aspx
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", EntryPoint = "GetModuleHandleExW", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
internal static extern bool GetModuleHandleEx(
[In] uint dwFlags,
[In] SafeLibraryHandle lpModuleName, // can point to a location within the module if GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS is set
[Out] out IntPtr phModule);
// http://msdn.microsoft.com/en-us/library/ms683212(v=vs.85).aspx
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
internal static extern IntPtr GetProcAddress(
[In] SafeLibraryHandle hModule,
[In, MarshalAs(UnmanagedType.LPStr)] string lpProcName);
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryExW", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
internal static extern SafeLibraryHandle LoadLibraryEx(
[In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
[In] IntPtr hFile,
[In] uint dwFlags);
internal static void ThrowExceptionForLastWin32Error()
{
int hr = Marshal.GetHRForLastWin32Error();
Marshal.ThrowExceptionForHR(hr);
}
}
}
}

View File

@ -0,0 +1,61 @@
// 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.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
namespace Microsoft.AspNetCore.Cryptography.SafeHandles
{
/// <summary>
/// Represents a handle returned by LocalAlloc.
/// The memory will be zeroed out before it's freed.
/// </summary>
internal unsafe sealed class SecureLocalAllocHandle : LocalAllocHandle
{
private readonly IntPtr _cb;
private SecureLocalAllocHandle(IntPtr cb)
{
_cb = cb;
}
public IntPtr Length
{
get
{
return _cb;
}
}
/// <summary>
/// Allocates some amount of memory using LocalAlloc.
/// </summary>
public static SecureLocalAllocHandle Allocate(IntPtr cb)
{
SecureLocalAllocHandle newHandle = new SecureLocalAllocHandle(cb);
newHandle.AllocateImpl(cb);
return newHandle;
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private void AllocateImpl(IntPtr cb)
{
handle = Marshal.AllocHGlobal(cb); // actually calls LocalAlloc
}
public SecureLocalAllocHandle Duplicate()
{
SecureLocalAllocHandle duplicateHandle = Allocate(_cb);
UnsafeBufferUtil.BlockCopy(from: this, to: duplicateHandle, length: _cb);
return duplicateHandle;
}
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
protected override bool ReleaseHandle()
{
UnsafeBufferUtil.SecureZeroMemory((byte*)handle, _cb); // compiler won't optimize this away
return base.ReleaseHandle();
}
}
}

View File

@ -0,0 +1,179 @@
// 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.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Threading;
using Microsoft.AspNetCore.Cryptography.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography
{
internal unsafe static class UnsafeBufferUtil
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void BlockCopy(void* from, void* to, int byteCount)
{
BlockCopy(from, to, checked((uint)byteCount)); // will be checked before invoking the delegate
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void BlockCopy(void* from, void* to, uint byteCount)
{
if (byteCount != 0)
{
BlockCopyCore((byte*)from, (byte*)to, byteCount);
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static void BlockCopy(LocalAllocHandle from, void* to, uint byteCount)
{
bool refAdded = false;
try
{
from.DangerousAddRef(ref refAdded);
BlockCopy((void*)from.DangerousGetHandle(), to, byteCount);
}
finally
{
if (refAdded)
{
from.DangerousRelease();
}
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static void BlockCopy(void* from, LocalAllocHandle to, uint byteCount)
{
bool refAdded = false;
try
{
to.DangerousAddRef(ref refAdded);
BlockCopy(from, (void*)to.DangerousGetHandle(), byteCount);
}
finally
{
if (refAdded)
{
to.DangerousRelease();
}
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static void BlockCopy(LocalAllocHandle from, LocalAllocHandle to, IntPtr length)
{
if (length == IntPtr.Zero)
{
return;
}
bool fromRefAdded = false;
bool toRefAdded = false;
try
{
from.DangerousAddRef(ref fromRefAdded);
to.DangerousAddRef(ref toRefAdded);
if (sizeof(IntPtr) == 4)
{
BlockCopyCore(from: (byte*)from.DangerousGetHandle(), to: (byte*)to.DangerousGetHandle(), byteCount: (uint)length.ToInt32());
}
else
{
BlockCopyCore(from: (byte*)from.DangerousGetHandle(), to: (byte*)to.DangerousGetHandle(), byteCount: (ulong)length.ToInt64());
}
}
finally
{
if (fromRefAdded)
{
from.DangerousRelease();
}
if (toRefAdded)
{
to.DangerousRelease();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void BlockCopyCore(byte* from, byte* to, uint byteCount)
{
Buffer.MemoryCopy(from, to, (ulong)byteCount, (ulong)byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void BlockCopyCore(byte* from, byte* to, ulong byteCount)
{
Buffer.MemoryCopy(from, to, byteCount, byteCount);
}
/// <summary>
/// Securely clears a memory buffer.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void SecureZeroMemory(byte* buffer, int byteCount)
{
SecureZeroMemory(buffer, checked((uint)byteCount));
}
/// <summary>
/// Securely clears a memory buffer.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void SecureZeroMemory(byte* buffer, uint byteCount)
{
if (byteCount != 0)
{
do
{
buffer[--byteCount] = 0;
} while (byteCount != 0);
// Volatile to make sure the zero-writes don't get optimized away
Volatile.Write(ref *buffer, 0);
}
}
/// <summary>
/// Securely clears a memory buffer.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void SecureZeroMemory(byte* buffer, ulong byteCount)
{
if (byteCount != 0)
{
do
{
buffer[--byteCount] = 0;
} while (byteCount != 0);
// Volatile to make sure the zero-writes don't get optimized away
Volatile.Write(ref *buffer, 0);
}
}
/// <summary>
/// Securely clears a memory buffer.
/// </summary>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void SecureZeroMemory(byte* buffer, IntPtr length)
{
if (sizeof(IntPtr) == 4)
{
SecureZeroMemory(buffer, (uint)length.ToInt32());
}
else
{
SecureZeroMemory(buffer, (ulong)length.ToInt64());
}
}
}
}

View File

@ -0,0 +1,346 @@
// 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.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Threading;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.SafeHandles;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography
{
[SuppressUnmanagedCodeSecurity]
internal unsafe static class UnsafeNativeMethods
{
private const string BCRYPT_LIB = "bcrypt.dll";
private static readonly Lazy<SafeLibraryHandle> _lazyBCryptLibHandle = GetLazyLibraryHandle(BCRYPT_LIB);
private const string CRYPT32_LIB = "crypt32.dll";
private static readonly Lazy<SafeLibraryHandle> _lazyCrypt32LibHandle = GetLazyLibraryHandle(CRYPT32_LIB);
private const string NCRYPT_LIB = "ncrypt.dll";
private static readonly Lazy<SafeLibraryHandle> _lazyNCryptLibHandle = GetLazyLibraryHandle(NCRYPT_LIB);
private static Lazy<SafeLibraryHandle> GetLazyLibraryHandle(string libraryName)
{
// We don't need to worry about race conditions: SafeLibraryHandle will clean up after itself
return new Lazy<SafeLibraryHandle>(() => SafeLibraryHandle.Open(libraryName), LazyThreadSafetyMode.PublicationOnly);
}
/*
* BCRYPT.DLL
*/
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375377(v=vs.85).aspx
internal static extern int BCryptCloseAlgorithmProvider(
[In] IntPtr hAlgorithm,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375383(v=vs.85).aspx
internal static extern int BCryptCreateHash(
[In] BCryptAlgorithmHandle hAlgorithm,
[Out] out BCryptHashHandle phHash,
[In] IntPtr pbHashObject,
[In] uint cbHashObject,
[In] byte* pbSecret,
[In] uint cbSecret,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375391(v=vs.85).aspx
internal static extern int BCryptDecrypt(
[In] BCryptKeyHandle hKey,
[In] byte* pbInput,
[In] uint cbInput,
[In] void* pPaddingInfo,
[In] byte* pbIV,
[In] uint cbIV,
[In] byte* pbOutput,
[In] uint cbOutput,
[Out] out uint pcbResult,
[In] BCryptEncryptFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd433795(v=vs.85).aspx
internal static extern int BCryptDeriveKeyPBKDF2(
[In] BCryptAlgorithmHandle hPrf,
[In] byte* pbPassword,
[In] uint cbPassword,
[In] byte* pbSalt,
[In] uint cbSalt,
[In] ulong cIterations,
[In] byte* pbDerivedKey,
[In] uint cbDerivedKey,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375399(v=vs.85).aspx
internal static extern int BCryptDestroyHash(
[In] IntPtr hHash);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375404(v=vs.85).aspx
internal static extern int BCryptDestroyKey(
[In] IntPtr hKey);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375413(v=vs.85).aspx
internal static extern int BCryptDuplicateHash(
[In] BCryptHashHandle hHash,
[Out] out BCryptHashHandle phNewHash,
[In] IntPtr pbHashObject,
[In] uint cbHashObject,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375421(v=vs.85).aspx
internal static extern int BCryptEncrypt(
[In] BCryptKeyHandle hKey,
[In] byte* pbInput,
[In] uint cbInput,
[In] void* pPaddingInfo,
[In] byte* pbIV,
[In] uint cbIV,
[In] byte* pbOutput,
[In] uint cbOutput,
[Out] out uint pcbResult,
[In] BCryptEncryptFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375443(v=vs.85).aspx
internal static extern int BCryptFinishHash(
[In] BCryptHashHandle hHash,
[In] byte* pbOutput,
[In] uint cbOutput,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375453(v=vs.85).aspx
internal static extern int BCryptGenerateSymmetricKey(
[In] BCryptAlgorithmHandle hAlgorithm,
[Out] out BCryptKeyHandle phKey,
[In] IntPtr pbKeyObject,
[In] uint cbKeyObject,
[In] byte* pbSecret,
[In] uint cbSecret,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375458(v=vs.85).aspx
internal static extern int BCryptGenRandom(
[In] IntPtr hAlgorithm,
[In] byte* pbBuffer,
[In] uint cbBuffer,
[In] BCryptGenRandomFlags dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375464(v=vs.85).aspx
internal static extern int BCryptGetProperty(
[In] BCryptHandle hObject,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszProperty,
[In] void* pbOutput,
[In] uint cbOutput,
[Out] out uint pcbResult,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375468(v=vs.85).aspx
internal static extern int BCryptHashData(
[In] BCryptHashHandle hHash,
[In] byte* pbInput,
[In] uint cbInput,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh448506(v=vs.85).aspx
internal static extern int BCryptKeyDerivation(
[In] BCryptKeyHandle hKey,
[In] BCryptBufferDesc* pParameterList,
[In] byte* pbDerivedKey,
[In] uint cbDerivedKey,
[Out] out uint pcbResult,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375479(v=vs.85).aspx
internal static extern int BCryptOpenAlgorithmProvider(
[Out] out BCryptAlgorithmHandle phAlgorithm,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAlgId,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszImplementation,
[In] uint dwFlags);
[DllImport(BCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375504(v=vs.85).aspx
internal static extern int BCryptSetProperty(
[In] BCryptHandle hObject,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszProperty,
[In] void* pbInput,
[In] uint cbInput,
[In] uint dwFlags);
/*
* CRYPT32.DLL
*/
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261(v=vs.85).aspx
internal static extern bool CryptProtectData(
[In] DATA_BLOB* pDataIn,
[In] IntPtr szDataDescr,
[In] DATA_BLOB* pOptionalEntropy,
[In] IntPtr pvReserved,
[In] IntPtr pPromptStruct,
[In] uint dwFlags,
[Out] out DATA_BLOB pDataOut);
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380262(v=vs.85).aspx
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool CryptProtectMemory(
[In] SafeHandle pData,
[In] uint cbData,
[In] uint dwFlags);
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380882(v=vs.85).aspx
internal static extern bool CryptUnprotectData(
[In] DATA_BLOB* pDataIn,
[In] IntPtr ppszDataDescr,
[In] DATA_BLOB* pOptionalEntropy,
[In] IntPtr pvReserved,
[In] IntPtr pPromptStruct,
[In] uint dwFlags,
[Out] out DATA_BLOB pDataOut);
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380890(v=vs.85).aspx
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool CryptUnprotectMemory(
[In] byte* pData,
[In] uint cbData,
[In] uint dwFlags);
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380890(v=vs.85).aspx
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool CryptUnprotectMemory(
[In] SafeHandle pData,
[In] uint cbData,
[In] uint dwFlags);
/*
* NCRYPT.DLL
*/
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706799(v=vs.85).aspx
internal static extern int NCryptCloseProtectionDescriptor(
[In] IntPtr hDescriptor);
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706800(v=vs.85).aspx
internal static extern int NCryptCreateProtectionDescriptor(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszDescriptorString,
[In] uint dwFlags,
[Out] out NCryptDescriptorHandle phDescriptor);
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh706801(v=vs.85).aspx
internal static extern int NCryptGetProtectionDescriptorInfo(
[In] NCryptDescriptorHandle hDescriptor,
[In] IntPtr pMemPara,
[In] uint dwInfoType,
[Out] out LocalAllocHandle ppvInfo);
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706802(v=vs.85).aspx
internal static extern int NCryptProtectSecret(
[In] NCryptDescriptorHandle hDescriptor,
[In] uint dwFlags,
[In] byte* pbData,
[In] uint cbData,
[In] IntPtr pMemPara,
[In] IntPtr hWnd,
[Out] out LocalAllocHandle ppbProtectedBlob,
[Out] out uint pcbProtectedBlob);
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706811(v=vs.85).aspx
internal static extern int NCryptUnprotectSecret(
[In] IntPtr phDescriptor,
[In] uint dwFlags,
[In] byte* pbProtectedBlob,
[In] uint cbProtectedBlob,
[In] IntPtr pMemPara,
[In] IntPtr hWnd,
[Out] out LocalAllocHandle ppbData,
[Out] out uint pcbData);
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706811(v=vs.85).aspx
internal static extern int NCryptUnprotectSecret(
[Out] out NCryptDescriptorHandle phDescriptor,
[In] uint dwFlags,
[In] byte* pbProtectedBlob,
[In] uint cbProtectedBlob,
[In] IntPtr pMemPara,
[In] IntPtr hWnd,
[Out] out LocalAllocHandle ppbData,
[Out] out uint pcbData);
/*
* HELPER FUNCTIONS
*/
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ThrowExceptionForBCryptStatus(int ntstatus)
{
// This wrapper method exists because 'throw' statements won't always be inlined.
if (ntstatus != 0)
{
ThrowExceptionForBCryptStatusImpl(ntstatus);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowExceptionForBCryptStatusImpl(int ntstatus)
{
string message = _lazyBCryptLibHandle.Value.FormatMessage(ntstatus);
throw new CryptographicException(message);
}
public static void ThrowExceptionForLastCrypt32Error()
{
int lastError = Marshal.GetLastWin32Error();
Debug.Assert(lastError != 0, "This method should only be called if there was an error.");
string message = _lazyCrypt32LibHandle.Value.FormatMessage(lastError);
throw new CryptographicException(message);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ThrowExceptionForNCryptStatus(int ntstatus)
{
// This wrapper method exists because 'throw' statements won't always be inlined.
if (ntstatus != 0)
{
ThrowExceptionForNCryptStatusImpl(ntstatus);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowExceptionForNCryptStatusImpl(int ntstatus)
{
string message = _lazyNCryptLibHandle.Value.FormatMessage(ntstatus);
throw new CryptographicException(message);
}
}
}

View File

@ -0,0 +1,59 @@
// 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.Diagnostics;
using System.Threading;
namespace Microsoft.AspNetCore.Cryptography
{
internal static class WeakReferenceHelpers
{
public static T GetSharedInstance<T>(ref WeakReference<T> weakReference, Func<T> factory)
where T : class, IDisposable
{
// First, see if the WR already exists and points to a live object.
WeakReference<T> existingWeakRef = Volatile.Read(ref weakReference);
T newTarget = null;
WeakReference<T> newWeakRef = null;
while (true)
{
if (existingWeakRef != null)
{
T existingTarget;
if (weakReference.TryGetTarget(out existingTarget))
{
// If we created a new target on a previous iteration of the loop but we
// weren't able to store the target into the desired location, dispose of it now.
newTarget?.Dispose();
return existingTarget;
}
}
// If the existing WR didn't point anywhere useful and this is our
// first iteration through the loop, create the new target and WR now.
if (newTarget == null)
{
newTarget = factory();
Debug.Assert(newTarget != null);
newWeakRef = new WeakReference<T>(newTarget);
}
Debug.Assert(newWeakRef != null);
// Try replacing the existing WR with our newly-created one.
WeakReference<T> currentWeakRef = Interlocked.CompareExchange(ref weakReference, newWeakRef, existingWeakRef);
if (ReferenceEquals(currentWeakRef, existingWeakRef))
{
// success, 'weakReference' now points to our newly-created WR
return newTarget;
}
// If we got to this point, somebody beat us to creating a new WR.
// We'll loop around and check it for validity.
Debug.Assert(currentWeakRef != null);
existingWeakRef = currentWeakRef;
}
}
}
}

View File

@ -0,0 +1,4 @@
{
"AssemblyIdentity": "Microsoft.AspNetCore.Cryptography.Internal, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": []
}

View File

@ -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;
using Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation
{
/// <summary>
/// Provides algorithms for performing key derivation.
/// </summary>
public static class KeyDerivation
{
/// <summary>
/// Performs key derivation using the PBKDF2 algorithm.
/// </summary>
/// <param name="password">The password from which to derive the key.</param>
/// <param name="salt">The salt to be used during the key derivation process.</param>
/// <param name="prf">The pseudo-random function to be used in the key derivation process.</param>
/// <param name="iterationCount">The number of iterations of the pseudo-random function to apply
/// during the key derivation process.</param>
/// <param name="numBytesRequested">The desired length (in bytes) of the derived key.</param>
/// <returns>The derived key.</returns>
/// <remarks>
/// The PBKDF2 algorithm is specified in RFC 2898.
/// </remarks>
public static byte[] Pbkdf2(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
{
if (password == null)
{
throw new ArgumentNullException(nameof(password));
}
if (salt == null)
{
throw new ArgumentNullException(nameof(salt));
}
// parameter checking
if (prf < KeyDerivationPrf.HMACSHA1 || prf > KeyDerivationPrf.HMACSHA512)
{
throw new ArgumentOutOfRangeException(nameof(prf));
}
if (iterationCount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(iterationCount));
}
if (numBytesRequested <= 0)
{
throw new ArgumentOutOfRangeException(nameof(numBytesRequested));
}
return Pbkdf2Util.Pbkdf2Provider.DeriveKey(password, salt, prf, iterationCount, numBytesRequested);
}
}
}

View File

@ -0,0 +1,26 @@
// 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.Cryptography.KeyDerivation
{
/// <summary>
/// Specifies the PRF which should be used for the key derivation algorithm.
/// </summary>
public enum KeyDerivationPrf
{
/// <summary>
/// The HMAC algorithm (RFC 2104) using the SHA-1 hash function (FIPS 180-4).
/// </summary>
HMACSHA1,
/// <summary>
/// The HMAC algorithm (RFC 2104) using the SHA-256 hash function (FIPS 180-4).
/// </summary>
HMACSHA256,
/// <summary>
/// The HMAC algorithm (RFC 2104) using the SHA-512 hash function (FIPS 180-4).
/// </summary>
HMACSHA512,
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core utilities for key derivation.</Description>
<TargetFrameworks>netstandard2.0;netcoreapp2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Cryptography.Internal\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
</ItemGroup>
</Project>

View File

@ -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;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2
{
/// <summary>
/// Internal interface used for abstracting away the PBKDF2 implementation since the implementation is OS-specific.
/// </summary>
internal interface IPbkdf2Provider
{
byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested);
}
}

View File

@ -0,0 +1,103 @@
// 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.Diagnostics;
using System.Security.Cryptography;
using System.Text;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2
{
/// <summary>
/// A PBKDF2 provider which utilizes the managed hash algorithm classes as PRFs.
/// This isn't the preferred provider since the implementation is slow, but it is provided as a fallback.
/// </summary>
internal sealed class ManagedPbkdf2Provider : IPbkdf2Provider
{
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
{
Debug.Assert(password != null);
Debug.Assert(salt != null);
Debug.Assert(iterationCount > 0);
Debug.Assert(numBytesRequested > 0);
// PBKDF2 is defined in NIST SP800-132, Sec. 5.3.
// http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
byte[] retVal = new byte[numBytesRequested];
int numBytesWritten = 0;
int numBytesRemaining = numBytesRequested;
// For each block index, U_0 := Salt || block_index
byte[] saltWithBlockIndex = new byte[checked(salt.Length + sizeof(uint))];
Buffer.BlockCopy(salt, 0, saltWithBlockIndex, 0, salt.Length);
using (var hashAlgorithm = PrfToManagedHmacAlgorithm(prf, password))
{
for (uint blockIndex = 1; numBytesRemaining > 0; blockIndex++)
{
// write the block index out as big-endian
saltWithBlockIndex[saltWithBlockIndex.Length - 4] = (byte)(blockIndex >> 24);
saltWithBlockIndex[saltWithBlockIndex.Length - 3] = (byte)(blockIndex >> 16);
saltWithBlockIndex[saltWithBlockIndex.Length - 2] = (byte)(blockIndex >> 8);
saltWithBlockIndex[saltWithBlockIndex.Length - 1] = (byte)blockIndex;
// U_1 = PRF(U_0) = PRF(Salt || block_index)
// T_blockIndex = U_1
byte[] U_iter = hashAlgorithm.ComputeHash(saltWithBlockIndex); // this is U_1
byte[] T_blockIndex = U_iter;
for (int iter = 1; iter < iterationCount; iter++)
{
U_iter = hashAlgorithm.ComputeHash(U_iter);
XorBuffers(src: U_iter, dest: T_blockIndex);
// At this point, the 'U_iter' variable actually contains U_{iter+1} (due to indexing differences).
}
// At this point, we're done iterating on this block, so copy the transformed block into retVal.
int numBytesToCopy = Math.Min(numBytesRemaining, T_blockIndex.Length);
Buffer.BlockCopy(T_blockIndex, 0, retVal, numBytesWritten, numBytesToCopy);
numBytesWritten += numBytesToCopy;
numBytesRemaining -= numBytesToCopy;
}
}
// retVal := T_1 || T_2 || ... || T_n, where T_n may be truncated to meet the desired output length
return retVal;
}
private static KeyedHashAlgorithm PrfToManagedHmacAlgorithm(KeyDerivationPrf prf, string password)
{
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
try
{
switch (prf)
{
case KeyDerivationPrf.HMACSHA1:
return new HMACSHA1(passwordBytes);
case KeyDerivationPrf.HMACSHA256:
return new HMACSHA256(passwordBytes);
case KeyDerivationPrf.HMACSHA512:
return new HMACSHA512(passwordBytes);
default:
throw CryptoUtil.Fail("Unrecognized PRF.");
}
}
finally
{
// The HMAC ctor makes a duplicate of this key; we clear original buffer to limit exposure to the GC.
Array.Clear(passwordBytes, 0, passwordBytes.Length);
}
}
private static void XorBuffers(byte[] src, byte[] dest)
{
// Note: dest buffer is mutated.
Debug.Assert(src.Length == dest.Length);
for (int i = 0; i < src.Length; i++)
{
dest[i] ^= src[i];
}
}
}
}

View File

@ -0,0 +1,71 @@
// 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
// Rfc2898DeriveBytes in .NET Standard 2.0 only supports SHA1
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2
{
/// <summary>
/// Implements Pbkdf2 using <see cref="Rfc2898DeriveBytes"/>.
/// </summary>
internal sealed class NetCorePbkdf2Provider : IPbkdf2Provider
{
private static readonly ManagedPbkdf2Provider _fallbackProvider = new ManagedPbkdf2Provider();
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
{
Debug.Assert(password != null);
Debug.Assert(salt != null);
Debug.Assert(iterationCount > 0);
Debug.Assert(numBytesRequested > 0);
if (salt.Length < 8)
{
// Rfc2898DeriveBytes enforces the 8 byte recommendation.
// To maintain compatibility, we call into ManagedPbkdf2Provider for salts shorter than 8 bytes
// because we can't use Rfc2898DeriveBytes with this salt.
return _fallbackProvider.DeriveKey(password, salt, prf, iterationCount, numBytesRequested);
}
else
{
return DeriveKeyImpl(password, salt, prf, iterationCount, numBytesRequested);
}
}
private static byte[] DeriveKeyImpl(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
{
HashAlgorithmName algorithmName;
switch (prf)
{
case KeyDerivationPrf.HMACSHA1:
algorithmName = HashAlgorithmName.SHA1;
break;
case KeyDerivationPrf.HMACSHA256:
algorithmName = HashAlgorithmName.SHA256;
break;
case KeyDerivationPrf.HMACSHA512:
algorithmName = HashAlgorithmName.SHA512;
break;
default:
throw new ArgumentOutOfRangeException();
}
var passwordBytes = Encoding.UTF8.GetBytes(password);
using (var rfc = new Rfc2898DeriveBytes(passwordBytes, salt, iterationCount, algorithmName))
{
return rfc.GetBytes(numBytesRequested);
}
}
}
}
#elif NETSTANDARD2_0
#else
#error Update target frameworks
#endif

View File

@ -0,0 +1,46 @@
// 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 Microsoft.AspNetCore.Cryptography.Cng;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2
{
/// <summary>
/// Internal base class used for abstracting away the PBKDF2 implementation since the implementation is OS-specific.
/// </summary>
internal static class Pbkdf2Util
{
public static readonly IPbkdf2Provider Pbkdf2Provider = GetPbkdf2Provider();
private static IPbkdf2Provider GetPbkdf2Provider()
{
// In priority order, our three implementations are Win8, Win7, and "other".
if (OSVersionUtil.IsWindows8OrLater())
{
// fastest implementation
return new Win8Pbkdf2Provider();
}
else if (OSVersionUtil.IsWindows())
{
// acceptable implementation
return new Win7Pbkdf2Provider();
}
#if NETCOREAPP2_0
else
{
// fastest implementation on .NET Core for Linux/macOS.
// Not supported on .NET Framework
return new NetCorePbkdf2Provider();
}
#elif NETSTANDARD2_0
else
{
// slowest implementation
return new ManagedPbkdf2Provider();
}
#else
#error Update target frameworks
#endif
}
}
}

View File

@ -0,0 +1,100 @@
// 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.Diagnostics;
using System.Text;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2
{
/// <summary>
/// A PBKDF2 provider which utilizes the Win7 API BCryptDeriveKeyPBKDF2.
/// </summary>
internal unsafe sealed class Win7Pbkdf2Provider : IPbkdf2Provider
{
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
{
Debug.Assert(password != null);
Debug.Assert(salt != null);
Debug.Assert(iterationCount > 0);
Debug.Assert(numBytesRequested > 0);
byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers
// Don't dispose of this algorithm instance; it is cached and reused!
var algHandle = PrfToCachedCngAlgorithmInstance(prf);
// Convert password string to bytes.
// Allocate on the stack whenever we can to save allocations.
int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length);
fixed (byte* pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbPasswordBuffer] : null)
{
byte* pbPasswordBuffer = pbHeapAllocatedPasswordBuffer;
if (pbPasswordBuffer == null)
{
if (cbPasswordBuffer == 0)
{
pbPasswordBuffer = &dummy;
}
else
{
byte* pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds
pbPasswordBuffer = pbStackAllocPasswordBuffer;
}
}
try
{
int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer
fixed (char* pszPassword = password)
{
cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer);
}
fixed (byte* pbHeapAllocatedSalt = salt)
{
byte* pbSalt = (pbHeapAllocatedSalt != null) ? pbHeapAllocatedSalt : &dummy;
byte[] retVal = new byte[numBytesRequested];
fixed (byte* pbRetVal = retVal)
{
int ntstatus = UnsafeNativeMethods.BCryptDeriveKeyPBKDF2(
hPrf: algHandle,
pbPassword: pbPasswordBuffer,
cbPassword: (uint)cbPasswordBufferUsed,
pbSalt: pbSalt,
cbSalt: (uint)salt.Length,
cIterations: (ulong)iterationCount,
pbDerivedKey: pbRetVal,
cbDerivedKey: (uint)retVal.Length,
dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
}
return retVal;
}
}
finally
{
UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer);
}
}
}
private static BCryptAlgorithmHandle PrfToCachedCngAlgorithmInstance(KeyDerivationPrf prf)
{
switch (prf)
{
case KeyDerivationPrf.HMACSHA1:
return CachedAlgorithmHandles.HMAC_SHA1;
case KeyDerivationPrf.HMACSHA256:
return CachedAlgorithmHandles.HMAC_SHA256;
case KeyDerivationPrf.HMACSHA512:
return CachedAlgorithmHandles.HMAC_SHA512;
default:
throw CryptoUtil.Fail("Unrecognized PRF.");
}
}
}
}

View File

@ -0,0 +1,211 @@
// 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.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.SafeHandles;
namespace Microsoft.AspNetCore.Cryptography.KeyDerivation.PBKDF2
{
/// <summary>
/// A PBKDF2 provider which utilizes the Win8 API BCryptKeyDerivation.
/// </summary>
internal unsafe sealed class Win8Pbkdf2Provider : IPbkdf2Provider
{
public byte[] DeriveKey(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
{
Debug.Assert(password != null);
Debug.Assert(salt != null);
Debug.Assert(iterationCount > 0);
Debug.Assert(numBytesRequested > 0);
string algorithmName = PrfToCngAlgorithmId(prf);
fixed (byte* pbHeapAllocatedSalt = salt)
{
byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers
byte* pbSalt = (pbHeapAllocatedSalt != null) ? pbHeapAllocatedSalt : &dummy;
byte[] retVal = new byte[numBytesRequested];
using (BCryptKeyHandle keyHandle = PasswordToPbkdfKeyHandle(password, CachedAlgorithmHandles.PBKDF2, prf))
{
fixed (byte* pbRetVal = retVal)
{
DeriveKeyCore(keyHandle, algorithmName, pbSalt, (uint)salt.Length, (ulong)iterationCount, pbRetVal, (uint)retVal.Length);
}
return retVal;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GetTotalByteLengthIncludingNullTerminator(string input)
{
if (input == null)
{
// degenerate case
return 0;
}
else
{
uint numChars = (uint)input.Length + 1U; // no overflow check necessary since Length is signed
return checked(numChars * sizeof(char));
}
}
private static BCryptKeyHandle PasswordToPbkdfKeyHandle(string password, BCryptAlgorithmHandle pbkdf2AlgHandle, KeyDerivationPrf prf)
{
byte dummy; // CLR doesn't like pinning zero-length buffers, so this provides a valid memory address when working with zero-length buffers
// Convert password string to bytes.
// Allocate on the stack whenever we can to save allocations.
int cbPasswordBuffer = Encoding.UTF8.GetMaxByteCount(password.Length);
fixed (byte* pbHeapAllocatedPasswordBuffer = (cbPasswordBuffer > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbPasswordBuffer] : null)
{
byte* pbPasswordBuffer = pbHeapAllocatedPasswordBuffer;
if (pbPasswordBuffer == null)
{
if (cbPasswordBuffer == 0)
{
pbPasswordBuffer = &dummy;
}
else
{
byte* pbStackAllocPasswordBuffer = stackalloc byte[cbPasswordBuffer]; // will be released when the frame unwinds
pbPasswordBuffer = pbStackAllocPasswordBuffer;
}
}
try
{
int cbPasswordBufferUsed; // we're not filling the entire buffer, just a partial buffer
fixed (char* pszPassword = password)
{
cbPasswordBufferUsed = Encoding.UTF8.GetBytes(pszPassword, password.Length, pbPasswordBuffer, cbPasswordBuffer);
}
return PasswordToPbkdfKeyHandleStep2(pbkdf2AlgHandle, pbPasswordBuffer, (uint)cbPasswordBufferUsed, prf);
}
finally
{
UnsafeBufferUtil.SecureZeroMemory(pbPasswordBuffer, cbPasswordBuffer);
}
}
}
private static BCryptKeyHandle PasswordToPbkdfKeyHandleStep2(BCryptAlgorithmHandle pbkdf2AlgHandle, byte* pbPassword, uint cbPassword, KeyDerivationPrf prf)
{
const uint PBKDF2_MAX_KEYLENGTH_IN_BYTES = 2048; // GetSupportedKeyLengths() on a Win8 box; value should never be lowered in any future version of Windows
if (cbPassword <= PBKDF2_MAX_KEYLENGTH_IN_BYTES)
{
// Common case: the password is small enough to be consumed directly by the PBKDF2 algorithm.
return pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword);
}
else
{
// Rare case: password is very long; we must hash manually.
// PBKDF2 uses the PRFs in HMAC mode, and when the HMAC input key exceeds the hash function's
// block length the key is hashed and run back through the key initialization function.
BCryptAlgorithmHandle prfAlgorithmHandle; // cached; don't dispose
switch (prf)
{
case KeyDerivationPrf.HMACSHA1:
prfAlgorithmHandle = CachedAlgorithmHandles.SHA1;
break;
case KeyDerivationPrf.HMACSHA256:
prfAlgorithmHandle = CachedAlgorithmHandles.SHA256;
break;
case KeyDerivationPrf.HMACSHA512:
prfAlgorithmHandle = CachedAlgorithmHandles.SHA512;
break;
default:
throw CryptoUtil.Fail("Unrecognized PRF.");
}
// Final sanity check: don't hash the password if the HMAC key initialization function wouldn't have done it for us.
if (cbPassword <= prfAlgorithmHandle.GetHashBlockLength() /* in bytes */)
{
return pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword);
}
// Hash the password and use the hash as input to PBKDF2.
uint cbPasswordDigest = prfAlgorithmHandle.GetHashDigestLength();
CryptoUtil.Assert(cbPasswordDigest > 0, "cbPasswordDigest > 0");
fixed (byte* pbPasswordDigest = new byte[cbPasswordDigest])
{
try
{
using (var hashHandle = prfAlgorithmHandle.CreateHash())
{
hashHandle.HashData(pbPassword, cbPassword, pbPasswordDigest, cbPasswordDigest);
}
return pbkdf2AlgHandle.GenerateSymmetricKey(pbPasswordDigest, cbPasswordDigest);
}
finally
{
UnsafeBufferUtil.SecureZeroMemory(pbPasswordDigest, cbPasswordDigest);
}
}
}
}
private static void DeriveKeyCore(BCryptKeyHandle pbkdf2KeyHandle, string hashAlgorithm, byte* pbSalt, uint cbSalt, ulong iterCount, byte* pbDerivedBytes, uint cbDerivedBytes)
{
// First, build the buffers necessary to pass (hash alg, salt, iter count) into the KDF
BCryptBuffer* pBuffers = stackalloc BCryptBuffer[3];
pBuffers[0].BufferType = BCryptKeyDerivationBufferType.KDF_ITERATION_COUNT;
pBuffers[0].pvBuffer = (IntPtr)(&iterCount);
pBuffers[0].cbBuffer = sizeof(ulong);
pBuffers[1].BufferType = BCryptKeyDerivationBufferType.KDF_SALT;
pBuffers[1].pvBuffer = (IntPtr)pbSalt;
pBuffers[1].cbBuffer = cbSalt;
fixed (char* pszHashAlgorithm = hashAlgorithm)
{
pBuffers[2].BufferType = BCryptKeyDerivationBufferType.KDF_HASH_ALGORITHM;
pBuffers[2].pvBuffer = (IntPtr)pszHashAlgorithm;
pBuffers[2].cbBuffer = GetTotalByteLengthIncludingNullTerminator(hashAlgorithm);
// Add the header which points to the buffers
BCryptBufferDesc bufferDesc = default(BCryptBufferDesc);
BCryptBufferDesc.Initialize(ref bufferDesc);
bufferDesc.cBuffers = 3;
bufferDesc.pBuffers = pBuffers;
// Finally, import the KDK into the KDF algorithm, then invoke the KDF
uint numBytesDerived;
int ntstatus = UnsafeNativeMethods.BCryptKeyDerivation(
hKey: pbkdf2KeyHandle,
pParameterList: &bufferDesc,
pbDerivedKey: pbDerivedBytes,
cbDerivedKey: cbDerivedBytes,
pcbResult: out numBytesDerived,
dwFlags: 0);
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
// Final sanity checks before returning control to caller.
CryptoUtil.Assert(numBytesDerived == cbDerivedBytes, "numBytesDerived == cbDerivedBytes");
}
}
private static string PrfToCngAlgorithmId(KeyDerivationPrf prf)
{
switch (prf)
{
case KeyDerivationPrf.HMACSHA1:
return Constants.BCRYPT_SHA1_ALGORITHM;
case KeyDerivationPrf.HMACSHA256:
return Constants.BCRYPT_SHA256_ALGORITHM;
case KeyDerivationPrf.HMACSHA512:
return Constants.BCRYPT_SHA512_ALGORITHM;
default:
throw CryptoUtil.Fail("Unrecognized PRF.");
}
}
}
}

View File

@ -0,0 +1,6 @@
// 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.Cryptography.KeyDerivation.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,78 @@
{
"AssemblyIdentity": "Microsoft.AspNetCore.Cryptography.KeyDerivation, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": [
{
"Name": "Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivation",
"Visibility": "Public",
"Kind": "Class",
"Abstract": true,
"Static": true,
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "Pbkdf2",
"Parameters": [
{
"Name": "password",
"Type": "System.String"
},
{
"Name": "salt",
"Type": "System.Byte[]"
},
{
"Name": "prf",
"Type": "Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivationPrf"
},
{
"Name": "iterationCount",
"Type": "System.Int32"
},
{
"Name": "numBytesRequested",
"Type": "System.Int32"
}
],
"ReturnType": "System.Byte[]",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivationPrf",
"Visibility": "Public",
"Kind": "Enumeration",
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Field",
"Name": "HMACSHA1",
"Parameters": [],
"GenericParameter": [],
"Literal": "0"
},
{
"Kind": "Field",
"Name": "HMACSHA256",
"Parameters": [],
"GenericParameter": [],
"Literal": "1"
},
{
"Kind": "Field",
"Name": "HMACSHA512",
"Parameters": [],
"GenericParameter": [],
"Literal": "2"
}
],
"GenericParameters": []
}
]
}

View File

@ -0,0 +1,33 @@
// 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.Diagnostics;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.DataProtection
{
internal static class CryptoUtil
{
// This isn't a typical Debug.Fail; an error always occurs, even in retail builds.
// This method doesn't return, but since the CLR doesn't allow specifying a 'never'
// return type, we mimic it by specifying our return type as Exception. That way
// callers can write 'throw Fail(...);' to make the C# compiler happy, as the
// throw keyword is implicitly of type O.
[MethodImpl(MethodImplOptions.NoInlining)]
public static Exception Fail(string message)
{
Debug.Fail(message);
throw new CryptographicException("Assertion failed: " + message);
}
// Allows callers to write "var x = Method() ?? Fail<T>(message);" as a convenience to guard
// against a method returning null unexpectedly.
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Fail<T>(string message) where T : class
{
throw Fail(message);
}
}
}

View File

@ -0,0 +1,244 @@
// 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 Microsoft.AspNetCore.DataProtection.Abstractions;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.DataProtection
{
/// <summary>
/// Helpful extension methods for data protection APIs.
/// </summary>
public static class DataProtectionCommonExtensions
{
/// <summary>
/// Creates an <see cref="IDataProtector"/> given a list of purposes.
/// </summary>
/// <param name="provider">The <see cref="IDataProtectionProvider"/> from which to generate the purpose chain.</param>
/// <param name="purposes">The list of purposes which contribute to the purpose chain. This list must
/// contain at least one element, and it may not contain null elements.</param>
/// <returns>An <see cref="IDataProtector"/> tied to the provided purpose chain.</returns>
/// <remarks>
/// This is a convenience method which chains together several calls to
/// <see cref="IDataProtectionProvider.CreateProtector(string)"/>. See that method's
/// documentation for more information.
/// </remarks>
public static IDataProtector CreateProtector(this IDataProtectionProvider provider, IEnumerable<string> purposes)
{
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
if (purposes == null)
{
throw new ArgumentNullException(nameof(purposes));
}
bool collectionIsEmpty = true;
IDataProtectionProvider retVal = provider;
foreach (string purpose in purposes)
{
if (purpose == null)
{
throw new ArgumentException(Resources.DataProtectionExtensions_NullPurposesCollection, nameof(purposes));
}
retVal = retVal.CreateProtector(purpose) ?? CryptoUtil.Fail<IDataProtector>("CreateProtector returned null.");
collectionIsEmpty = false;
}
if (collectionIsEmpty)
{
throw new ArgumentException(Resources.DataProtectionExtensions_NullPurposesCollection, nameof(purposes));
}
Debug.Assert(retVal is IDataProtector); // CreateProtector is supposed to return an instance of this interface
return (IDataProtector)retVal;
}
/// <summary>
/// Creates an <see cref="IDataProtector"/> given a list of purposes.
/// </summary>
/// <param name="provider">The <see cref="IDataProtectionProvider"/> from which to generate the purpose chain.</param>
/// <param name="purpose">The primary purpose used to create the <see cref="IDataProtector"/>.</param>
/// <param name="subPurposes">An optional list of secondary purposes which contribute to the purpose chain.
/// If this list is provided it cannot contain null elements.</param>
/// <returns>An <see cref="IDataProtector"/> tied to the provided purpose chain.</returns>
/// <remarks>
/// This is a convenience method which chains together several calls to
/// <see cref="IDataProtectionProvider.CreateProtector(string)"/>. See that method's
/// documentation for more information.
/// </remarks>
public static IDataProtector CreateProtector(this IDataProtectionProvider provider, string purpose, params string[] subPurposes)
{
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
if (purpose == null)
{
throw new ArgumentNullException(nameof(purpose));
}
// The method signature isn't simply CreateProtector(this IDataProtectionProvider, params string[] purposes)
// because we don't want the code provider.CreateProtector() [parameterless] to inadvertently compile.
// The actual signature for this method forces at least one purpose to be provided at the call site.
IDataProtector protector = provider.CreateProtector(purpose);
if (subPurposes != null && subPurposes.Length > 0)
{
protector = protector?.CreateProtector((IEnumerable<string>)subPurposes);
}
return protector ?? CryptoUtil.Fail<IDataProtector>("CreateProtector returned null.");
}
/// <summary>
/// Retrieves an <see cref="IDataProtectionProvider"/> from an <see cref="IServiceProvider"/>.
/// </summary>
/// <param name="services">The service provider from which to retrieve the <see cref="IDataProtectionProvider"/>.</param>
/// <returns>An <see cref="IDataProtectionProvider"/>. This method is guaranteed never to return null.</returns>
/// <exception cref="InvalidOperationException">If no <see cref="IDataProtectionProvider"/> service exists in <paramref name="services"/>.</exception>
public static IDataProtectionProvider GetDataProtectionProvider(this IServiceProvider services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
// We have our own implementation of GetRequiredService<T> since we don't want to
// take a dependency on DependencyInjection.Interfaces.
IDataProtectionProvider provider = (IDataProtectionProvider)services.GetService(typeof(IDataProtectionProvider));
if (provider == null)
{
throw new InvalidOperationException(Resources.FormatDataProtectionExtensions_NoService(typeof(IDataProtectionProvider).FullName));
}
return provider;
}
/// <summary>
/// Retrieves an <see cref="IDataProtector"/> from an <see cref="IServiceProvider"/> given a list of purposes.
/// </summary>
/// <param name="services">An <see cref="IServiceProvider"/> which contains the <see cref="IDataProtectionProvider"/>
/// from which to generate the purpose chain.</param>
/// <param name="purposes">The list of purposes which contribute to the purpose chain. This list must
/// contain at least one element, and it may not contain null elements.</param>
/// <returns>An <see cref="IDataProtector"/> tied to the provided purpose chain.</returns>
/// <remarks>
/// This is a convenience method which calls <see cref="GetDataProtectionProvider(IServiceProvider)"/>
/// then <see cref="CreateProtector(IDataProtectionProvider, IEnumerable{string})"/>. See those methods'
/// documentation for more information.
/// </remarks>
public static IDataProtector GetDataProtector(this IServiceProvider services, IEnumerable<string> purposes)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (purposes == null)
{
throw new ArgumentNullException(nameof(purposes));
}
return services.GetDataProtectionProvider().CreateProtector(purposes);
}
/// <summary>
/// Retrieves an <see cref="IDataProtector"/> from an <see cref="IServiceProvider"/> given a list of purposes.
/// </summary>
/// <param name="services">An <see cref="IServiceProvider"/> which contains the <see cref="IDataProtectionProvider"/>
/// from which to generate the purpose chain.</param>
/// <param name="purpose">The primary purpose used to create the <see cref="IDataProtector"/>.</param>
/// <param name="subPurposes">An optional list of secondary purposes which contribute to the purpose chain.
/// If this list is provided it cannot contain null elements.</param>
/// <returns>An <see cref="IDataProtector"/> tied to the provided purpose chain.</returns>
/// <remarks>
/// This is a convenience method which calls <see cref="GetDataProtectionProvider(IServiceProvider)"/>
/// then <see cref="CreateProtector(IDataProtectionProvider, string, string[])"/>. See those methods'
/// documentation for more information.
/// </remarks>
public static IDataProtector GetDataProtector(this IServiceProvider services, string purpose, params string[] subPurposes)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (purpose == null)
{
throw new ArgumentNullException(nameof(purpose));
}
return services.GetDataProtectionProvider().CreateProtector(purpose, subPurposes);
}
/// <summary>
/// Cryptographically protects a piece of plaintext data.
/// </summary>
/// <param name="protector">The data protector to use for this operation.</param>
/// <param name="plaintext">The plaintext data to protect.</param>
/// <returns>The protected form of the plaintext data.</returns>
public static string Protect(this IDataProtector protector, string plaintext)
{
if (protector == null)
{
throw new ArgumentNullException(nameof(protector));
}
if (plaintext == null)
{
throw new ArgumentNullException(nameof(plaintext));
}
try
{
byte[] plaintextAsBytes = EncodingUtil.SecureUtf8Encoding.GetBytes(plaintext);
byte[] protectedDataAsBytes = protector.Protect(plaintextAsBytes);
return WebEncoders.Base64UrlEncode(protectedDataAsBytes);
}
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize exceptions to CryptographicException
throw Error.CryptCommon_GenericError(ex);
}
}
/// <summary>
/// Cryptographically unprotects a piece of protected data.
/// </summary>
/// <param name="protector">The data protector to use for this operation.</param>
/// <param name="protectedData">The protected data to unprotect.</param>
/// <returns>The plaintext form of the protected data.</returns>
/// <exception cref="System.Security.Cryptography.CryptographicException">
/// Thrown if <paramref name="protectedData"/> is invalid or malformed.
/// </exception>
public static string Unprotect(this IDataProtector protector, string protectedData)
{
if (protector == null)
{
throw new ArgumentNullException(nameof(protector));
}
if (protectedData == null)
{
throw new ArgumentNullException(nameof(protectedData));
}
try
{
byte[] protectedDataAsBytes = WebEncoders.Base64UrlDecode(protectedData);
byte[] plaintextAsBytes = protector.Unprotect(protectedDataAsBytes);
return EncodingUtil.SecureUtf8Encoding.GetString(plaintextAsBytes);
}
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize exceptions to CryptographicException
throw Error.CryptCommon_GenericError(ex);
}
}
}
}

View File

@ -0,0 +1,23 @@
// 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.Security.Cryptography;
using Microsoft.AspNetCore.DataProtection.Abstractions;
namespace Microsoft.AspNetCore.DataProtection
{
internal static class Error
{
public static CryptographicException CryptCommon_GenericError(Exception inner = null)
{
return new CryptographicException(Resources.CryptCommon_GenericError, inner);
}
public static CryptographicException CryptCommon_PayloadInvalid()
{
string message = Resources.CryptCommon_PayloadInvalid;
return new CryptographicException(message);
}
}
}

View File

@ -0,0 +1,26 @@
// 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.DataProtection
{
/// <summary>
/// An interface that can be used to create <see cref="IDataProtector"/> instances.
/// </summary>
public interface IDataProtectionProvider
{
/// <summary>
/// Creates an <see cref="IDataProtector"/> given a purpose.
/// </summary>
/// <param name="purpose">
/// The purpose to be assigned to the newly-created <see cref="IDataProtector"/>.
/// </param>
/// <returns>An IDataProtector tied to the provided purpose.</returns>
/// <remarks>
/// The <paramref name="purpose"/> parameter must be unique for the intended use case; two
/// different <see cref="IDataProtector"/> instances created with two different <paramref name="purpose"/>
/// values will not be able to decipher each other's payloads. The <paramref name="purpose"/> parameter
/// value is not intended to be kept secret.
/// </remarks>
IDataProtector CreateProtector(string purpose);
}
}

View File

@ -0,0 +1,28 @@
// 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.DataProtection
{
/// <summary>
/// An interface that can provide data protection services.
/// </summary>
public interface IDataProtector : IDataProtectionProvider
{
/// <summary>
/// Cryptographically protects a piece of plaintext data.
/// </summary>
/// <param name="plaintext">The plaintext data to protect.</param>
/// <returns>The protected form of the plaintext data.</returns>
byte[] Protect(byte[] plaintext);
/// <summary>
/// Cryptographically unprotects a piece of protected data.
/// </summary>
/// <param name="protectedData">The protected data to unprotect.</param>
/// <returns>The plaintext form of the protected data.</returns>
/// <exception cref="System.Security.Cryptography.CryptographicException">
/// Thrown if the protected data is invalid or malformed.
/// </exception>
byte[] Unprotect(byte[] protectedData);
}
}

View File

@ -0,0 +1,25 @@
// 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.ComponentModel;
namespace Microsoft.AspNetCore.DataProtection.Infrastructure
{
/// <summary>
/// Provides information used to discriminate applications.
/// </summary>
/// <remarks>
/// This type supports the data protection system and is not intended to be used
/// by consumers.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IApplicationDiscriminator
{
/// <summary>
/// An identifier that uniquely discriminates this application from all other
/// applications on the machine.
/// </summary>
string Discriminator { get; }
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core data protection abstractions.
Commonly used types:
Microsoft.AspNetCore.DataProtection.IDataProtectionProvider
Microsoft.AspNetCore.DataProtection.IDataProtector</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection</PackageTags>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\shared\*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.WebEncoders.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -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;
// for unit testing
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection.Abstractions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,86 @@
// <auto-generated />
namespace Microsoft.AspNetCore.DataProtection.Abstractions
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNetCore.DataProtection.Abstractions.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The payload was invalid.
/// </summary>
internal static string CryptCommon_PayloadInvalid
{
get => GetString("CryptCommon_PayloadInvalid");
}
/// <summary>
/// The payload was invalid.
/// </summary>
internal static string FormatCryptCommon_PayloadInvalid()
=> GetString("CryptCommon_PayloadInvalid");
/// <summary>
/// The purposes collection cannot be null or empty and cannot contain null elements.
/// </summary>
internal static string DataProtectionExtensions_NullPurposesCollection
{
get => GetString("DataProtectionExtensions_NullPurposesCollection");
}
/// <summary>
/// The purposes collection cannot be null or empty and cannot contain null elements.
/// </summary>
internal static string FormatDataProtectionExtensions_NullPurposesCollection()
=> GetString("DataProtectionExtensions_NullPurposesCollection");
/// <summary>
/// An error occurred during a cryptographic operation.
/// </summary>
internal static string CryptCommon_GenericError
{
get => GetString("CryptCommon_GenericError");
}
/// <summary>
/// An error occurred during a cryptographic operation.
/// </summary>
internal static string FormatCryptCommon_GenericError()
=> GetString("CryptCommon_GenericError");
/// <summary>
/// No service for type '{0}' has been registered.
/// </summary>
internal static string DataProtectionExtensions_NoService
{
get => GetString("DataProtectionExtensions_NoService");
}
/// <summary>
/// No service for type '{0}' has been registered.
/// </summary>
internal static string FormatDataProtectionExtensions_NoService(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("DataProtectionExtensions_NoService"), p0);
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CryptCommon_PayloadInvalid" xml:space="preserve">
<value>The payload was invalid.</value>
</data>
<data name="DataProtectionExtensions_NullPurposesCollection" xml:space="preserve">
<value>The purposes collection cannot be null or empty and cannot contain null elements.</value>
</data>
<data name="CryptCommon_GenericError" xml:space="preserve">
<value>An error occurred during a cryptographic operation.</value>
</data>
<data name="DataProtectionExtensions_NoService" xml:space="preserve">
<value>No service for type '{0}' has been registered.</value>
</data>
</root>

View File

@ -0,0 +1,231 @@
{
"AssemblyIdentity": "Microsoft.AspNetCore.DataProtection.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": [
{
"Name": "Microsoft.AspNetCore.DataProtection.DataProtectionCommonExtensions",
"Visibility": "Public",
"Kind": "Class",
"Abstract": true,
"Static": true,
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "CreateProtector",
"Parameters": [
{
"Name": "provider",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider"
},
{
"Name": "purposes",
"Type": "System.Collections.Generic.IEnumerable<System.String>"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CreateProtector",
"Parameters": [
{
"Name": "provider",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider"
},
{
"Name": "purpose",
"Type": "System.String"
},
{
"Name": "subPurposes",
"Type": "System.String[]",
"IsParams": true
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "GetDataProtectionProvider",
"Parameters": [
{
"Name": "services",
"Type": "System.IServiceProvider"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "GetDataProtector",
"Parameters": [
{
"Name": "services",
"Type": "System.IServiceProvider"
},
{
"Name": "purposes",
"Type": "System.Collections.Generic.IEnumerable<System.String>"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "GetDataProtector",
"Parameters": [
{
"Name": "services",
"Type": "System.IServiceProvider"
},
{
"Name": "purpose",
"Type": "System.String"
},
{
"Name": "subPurposes",
"Type": "System.String[]",
"IsParams": true
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Protect",
"Parameters": [
{
"Name": "protector",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtector"
},
{
"Name": "plaintext",
"Type": "System.String"
}
],
"ReturnType": "System.String",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unprotect",
"Parameters": [
{
"Name": "protector",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtector"
},
{
"Name": "protectedData",
"Type": "System.String"
}
],
"ReturnType": "System.String",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider",
"Visibility": "Public",
"Kind": "Interface",
"Abstract": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "CreateProtector",
"Parameters": [
{
"Name": "purpose",
"Type": "System.String"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.DataProtection.IDataProtector",
"Visibility": "Public",
"Kind": "Interface",
"Abstract": true,
"ImplementedInterfaces": [
"Microsoft.AspNetCore.DataProtection.IDataProtectionProvider"
],
"Members": [
{
"Kind": "Method",
"Name": "Protect",
"Parameters": [
{
"Name": "plaintext",
"Type": "System.Byte[]"
}
],
"ReturnType": "System.Byte[]",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unprotect",
"Parameters": [
{
"Name": "protectedData",
"Type": "System.Byte[]"
}
],
"ReturnType": "System.Byte[]",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.DataProtection.Infrastructure.IApplicationDiscriminator",
"Visibility": "Public",
"Kind": "Interface",
"Abstract": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Discriminator",
"Parameters": [],
"ReturnType": "System.String",
"GenericParameter": []
}
],
"GenericParameters": []
}
]
}

View File

@ -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.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection.AzureKeyVault;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Azure.KeyVault;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace Microsoft.AspNetCore.DataProtection
{
/// <summary>
/// Contains Azure KeyVault-specific extension methods for modifying a <see cref="IDataProtectionBuilder"/>.
/// </summary>
public static class AzureDataProtectionBuilderExtensions
{
/// <summary>
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="keyIdentifier">The Azure KeyVault key identifier used for key encryption.</param>
/// <param name="clientId">The application client id.</param>
/// <param name="certificate"></param>
/// <returns>The value <paramref name="builder"/>.</returns>
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, string clientId, X509Certificate2 certificate)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentException(nameof(clientId));
}
if (certificate == null)
{
throw new ArgumentNullException(nameof(certificate));
}
KeyVaultClient.AuthenticationCallback callback =
(authority, resource, scope) => GetTokenFromClientCertificate(authority, resource, clientId, certificate);
return ProtectKeysWithAzureKeyVault(builder, new KeyVaultClient(callback), keyIdentifier);
}
private static async Task<string> GetTokenFromClientCertificate(string authority, string resource, string clientId, X509Certificate2 certificate)
{
var authContext = new AuthenticationContext(authority);
var result = await authContext.AcquireTokenAsync(resource, new ClientAssertionCertificate(clientId, certificate));
return result.AccessToken;
}
/// <summary>
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="keyIdentifier">The Azure KeyVault key identifier used for key encryption.</param>
/// <param name="clientId">The application client id.</param>
/// <param name="clientSecret">The client secret to use for authentication.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, string clientId, string clientSecret)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentNullException(nameof(clientId));
}
if (string.IsNullOrEmpty(clientSecret))
{
throw new ArgumentNullException(nameof(clientSecret));
}
KeyVaultClient.AuthenticationCallback callback =
(authority, resource, scope) => GetTokenFromClientSecret(authority, resource, clientId, clientSecret);
return ProtectKeysWithAzureKeyVault(builder, new KeyVaultClient(callback), keyIdentifier);
}
private static async Task<string> GetTokenFromClientSecret(string authority, string resource, string clientId, string clientSecret)
{
var authContext = new AuthenticationContext(authority);
var clientCred = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenAsync(resource, clientCred);
return result.AccessToken;
}
/// <summary>
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="client">The <see cref="KeyVaultClient"/> to use for KeyVault access.</param>
/// <param name="keyIdentifier">The Azure KeyVault key identifier used for key encryption.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, KeyVaultClient client, string keyIdentifier)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}
if (string.IsNullOrEmpty(keyIdentifier))
{
throw new ArgumentException(nameof(keyIdentifier));
}
var vaultClientWrapper = new KeyVaultClientWrapper(client);
builder.Services.AddSingleton<IKeyVaultWrappingClient>(vaultClientWrapper);
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.XmlEncryptor = new AzureKeyVaultXmlEncryptor(vaultClientWrapper, keyIdentifier);
});
return builder;
}
}
}

View File

@ -0,0 +1,52 @@
// 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.IO;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal class AzureKeyVaultXmlDecryptor: IXmlDecryptor
{
private readonly IKeyVaultWrappingClient _client;
public AzureKeyVaultXmlDecryptor(IServiceProvider serviceProvider)
{
_client = serviceProvider.GetService<IKeyVaultWrappingClient>();
}
public XElement Decrypt(XElement encryptedElement)
{
return DecryptAsync(encryptedElement).GetAwaiter().GetResult();
}
private async Task<XElement> DecryptAsync(XElement encryptedElement)
{
var kid = (string)encryptedElement.Element("kid");
var symmetricKey = Convert.FromBase64String((string)encryptedElement.Element("key"));
var symmetricIV = Convert.FromBase64String((string)encryptedElement.Element("iv"));
var encryptedValue = Convert.FromBase64String((string)encryptedElement.Element("value"));
var result = await _client.UnwrapKeyAsync(kid, AzureKeyVaultXmlEncryptor.DefaultKeyEncryption, symmetricKey);
byte[] decryptedValue;
using (var symmetricAlgorithm = AzureKeyVaultXmlEncryptor.DefaultSymmetricAlgorithmFactory())
{
using (var decryptor = symmetricAlgorithm.CreateDecryptor(result.Result, symmetricIV))
{
decryptedValue = decryptor.TransformFinalBlock(encryptedValue, 0, encryptedValue.Length);
}
}
using (var memoryStream = new MemoryStream(decryptedValue))
{
return XElement.Load(memoryStream);
}
}
}
}

View File

@ -0,0 +1,77 @@
// 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.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Azure.KeyVault.WebKey;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal class AzureKeyVaultXmlEncryptor : IXmlEncryptor
{
internal static string DefaultKeyEncryption = JsonWebKeyEncryptionAlgorithm.RSAOAEP;
internal static Func<SymmetricAlgorithm> DefaultSymmetricAlgorithmFactory = Aes.Create;
private readonly RandomNumberGenerator _randomNumberGenerator;
private readonly IKeyVaultWrappingClient _client;
private readonly string _keyId;
public AzureKeyVaultXmlEncryptor(IKeyVaultWrappingClient client, string keyId)
: this(client, keyId, RandomNumberGenerator.Create())
{
}
internal AzureKeyVaultXmlEncryptor(IKeyVaultWrappingClient client, string keyId, RandomNumberGenerator randomNumberGenerator)
{
_client = client;
_keyId = keyId;
_randomNumberGenerator = randomNumberGenerator;
}
public EncryptedXmlInfo Encrypt(XElement plaintextElement)
{
return EncryptAsync(plaintextElement).GetAwaiter().GetResult();
}
private async Task<EncryptedXmlInfo> EncryptAsync(XElement plaintextElement)
{
byte[] value;
using (var memoryStream = new MemoryStream())
{
plaintextElement.Save(memoryStream, SaveOptions.DisableFormatting);
value = memoryStream.ToArray();
}
using (var symmetricAlgorithm = DefaultSymmetricAlgorithmFactory())
{
var symmetricBlockSize = symmetricAlgorithm.BlockSize / 8;
var symmetricKey = new byte[symmetricBlockSize];
var symmetricIV = new byte[symmetricBlockSize];
_randomNumberGenerator.GetBytes(symmetricKey);
_randomNumberGenerator.GetBytes(symmetricIV);
byte[] encryptedValue;
using (var encryptor = symmetricAlgorithm.CreateEncryptor(symmetricKey, symmetricIV))
{
encryptedValue = encryptor.TransformFinalBlock(value, 0, value.Length);
}
var wrappedKey = await _client.WrapKeyAsync(_keyId, DefaultKeyEncryption, symmetricKey);
var element = new XElement("encryptedKey",
new XComment(" This key is encrypted with Azure KeyVault. "),
new XElement("kid", wrappedKey.Kid),
new XElement("key", Convert.ToBase64String(wrappedKey.Result)),
new XElement("iv", Convert.ToBase64String(symmetricIV)),
new XElement("value", Convert.ToBase64String(encryptedValue)));
return new EncryptedXmlInfo(element, typeof(AzureKeyVaultXmlDecryptor));
}
}
}
}

View File

@ -0,0 +1,14 @@
// 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.Threading.Tasks;
using Microsoft.Azure.KeyVault.Models;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal interface IKeyVaultWrappingClient
{
Task<KeyOperationResult> UnwrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText);
Task<KeyOperationResult> WrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText);
}
}

View File

@ -0,0 +1,29 @@
// 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.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal class KeyVaultClientWrapper : IKeyVaultWrappingClient
{
private readonly KeyVaultClient _client;
public KeyVaultClientWrapper(KeyVaultClient client)
{
_client = client;
}
public Task<KeyOperationResult> UnwrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText)
{
return _client.UnwrapKeyAsync(keyIdentifier, algorithm, cipherText);
}
public Task<KeyOperationResult> WrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText)
{
return _client.WrapKeyAsync(keyIdentifier, algorithm, cipherText);
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Microsoft Azure KeyVault key encryption support.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection;azure;keyvault</PackageTags>
<EnableApiCheck>false</EnableApiCheck>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryPackageVersion)" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="$(MicrosoftAzureKeyVaultPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
// 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.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@ -0,0 +1,297 @@
// 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.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
namespace Microsoft.AspNetCore.DataProtection.AzureStorage
{
/// <summary>
/// An <see cref="IXmlRepository"/> which is backed by Azure Blob Storage.
/// </summary>
/// <remarks>
/// Instances of this type are thread-safe.
/// </remarks>
public sealed class AzureBlobXmlRepository : IXmlRepository
{
private const int ConflictMaxRetries = 5;
private static readonly TimeSpan ConflictBackoffPeriod = TimeSpan.FromMilliseconds(200);
private static readonly XName RepositoryElementName = "repository";
private readonly Func<ICloudBlob> _blobRefFactory;
private readonly Random _random;
private BlobData _cachedBlobData;
/// <summary>
/// Creates a new instance of the <see cref="AzureBlobXmlRepository"/>.
/// </summary>
/// <param name="blobRefFactory">A factory which can create <see cref="ICloudBlob"/>
/// instances. The factory must be thread-safe for invocation by multiple
/// concurrent threads, and each invocation must return a new object.</param>
public AzureBlobXmlRepository(Func<ICloudBlob> blobRefFactory)
{
if (blobRefFactory == null)
{
throw new ArgumentNullException(nameof(blobRefFactory));
}
_blobRefFactory = blobRefFactory;
_random = new Random();
}
/// <inheritdoc />
public IReadOnlyCollection<XElement> GetAllElements()
{
var blobRef = CreateFreshBlobRef();
// Shunt the work onto a ThreadPool thread so that it's independent of any
// existing sync context or other potentially deadlock-causing items.
var elements = Task.Run(() => GetAllElementsAsync(blobRef)).GetAwaiter().GetResult();
return new ReadOnlyCollection<XElement>(elements);
}
/// <inheritdoc />
public void StoreElement(XElement element, string friendlyName)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
var blobRef = CreateFreshBlobRef();
// Shunt the work onto a ThreadPool thread so that it's independent of any
// existing sync context or other potentially deadlock-causing items.
Task.Run(() => StoreElementAsync(blobRef, element)).GetAwaiter().GetResult();
}
private XDocument CreateDocumentFromBlob(byte[] blob)
{
using (var memoryStream = new MemoryStream(blob))
{
var xmlReaderSettings = new XmlReaderSettings()
{
DtdProcessing = DtdProcessing.Prohibit, IgnoreProcessingInstructions = true
};
using (var xmlReader = XmlReader.Create(memoryStream, xmlReaderSettings))
{
return XDocument.Load(xmlReader);
}
}
}
private ICloudBlob CreateFreshBlobRef()
{
// ICloudBlob instances aren't thread-safe, so we need to make sure we're working
// with a fresh instance that won't be mutated by another thread.
var blobRef = _blobRefFactory();
if (blobRef == null)
{
throw new InvalidOperationException("The ICloudBlob factory method returned null.");
}
return blobRef;
}
private async Task<IList<XElement>> GetAllElementsAsync(ICloudBlob blobRef)
{
var data = await GetLatestDataAsync(blobRef);
if (data == null)
{
// no data in blob storage
return new XElement[0];
}
// The document will look like this:
//
// <root>
// <child />
// <child />
// ...
// </root>
//
// We want to return the first-level child elements to our caller.
var doc = CreateDocumentFromBlob(data.BlobContents);
return doc.Root.Elements().ToList();
}
private async Task<BlobData> GetLatestDataAsync(ICloudBlob blobRef)
{
// Set the appropriate AccessCondition based on what we believe the latest
// file contents to be, then make the request.
var latestCachedData = Volatile.Read(ref _cachedBlobData); // local ref so field isn't mutated under our feet
var accessCondition = (latestCachedData != null)
? AccessCondition.GenerateIfNoneMatchCondition(latestCachedData.ETag)
: null;
try
{
using (var memoryStream = new MemoryStream())
{
await blobRef.DownloadToStreamAsync(
target: memoryStream,
accessCondition: accessCondition,
options: null,
operationContext: null);
// At this point, our original cache either didn't exist or was outdated.
// We'll update it now and return the updated value;
latestCachedData = new BlobData()
{
BlobContents = memoryStream.ToArray(),
ETag = blobRef.Properties.ETag
};
}
Volatile.Write(ref _cachedBlobData, latestCachedData);
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 304)
{
// 304 Not Modified
// Thrown when we already have the latest cached data.
// This isn't an error; we'll return our cached copy of the data.
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404)
{
// 404 Not Found
// Thrown when no file exists in storage.
// This isn't an error; we'll delete our cached copy of data.
latestCachedData = null;
Volatile.Write(ref _cachedBlobData, latestCachedData);
}
return latestCachedData;
}
private int GetRandomizedBackoffPeriod()
{
// returns a TimeSpan in the range [0.8, 1.0) * ConflictBackoffPeriod
// not used for crypto purposes
var multiplier = 0.8 + (_random.NextDouble() * 0.2);
return (int) (multiplier * ConflictBackoffPeriod.Ticks);
}
private async Task StoreElementAsync(ICloudBlob blobRef, XElement element)
{
// holds the last error in case we need to rethrow it
ExceptionDispatchInfo lastError = null;
for (var i = 0; i < ConflictMaxRetries; i++)
{
if (i > 1)
{
// If multiple conflicts occurred, wait a small period of time before retrying
// the operation so that other writers can make forward progress.
await Task.Delay(GetRandomizedBackoffPeriod());
}
if (i > 0)
{
// If at least one conflict occurred, make sure we have an up-to-date
// view of the blob contents.
await GetLatestDataAsync(blobRef);
}
// Merge the new element into the document. If no document exists,
// create a new default document and inject this element into it.
var latestData = Volatile.Read(ref _cachedBlobData);
var doc = (latestData != null)
? CreateDocumentFromBlob(latestData.BlobContents)
: new XDocument(new XElement(RepositoryElementName));
doc.Root.Add(element);
// Turn this document back into a byte[].
var serializedDoc = new MemoryStream();
doc.Save(serializedDoc, SaveOptions.DisableFormatting);
// Generate the appropriate precondition header based on whether or not
// we believe data already exists in storage.
AccessCondition accessCondition;
if (latestData != null)
{
accessCondition = AccessCondition.GenerateIfMatchCondition(blobRef.Properties.ETag);
}
else
{
accessCondition = AccessCondition.GenerateIfNotExistsCondition();
blobRef.Properties.ContentType = "application/xml; charset=utf-8"; // set content type on first write
}
try
{
// Send the request up to the server.
var serializedDocAsByteArray = serializedDoc.ToArray();
await blobRef.UploadFromByteArrayAsync(
buffer: serializedDocAsByteArray,
index: 0,
count: serializedDocAsByteArray.Length,
accessCondition: accessCondition,
options: null,
operationContext: null);
// If we got this far, success!
// We can update the cached view of the remote contents.
Volatile.Write(ref _cachedBlobData, new BlobData()
{
BlobContents = serializedDocAsByteArray,
ETag = blobRef.Properties.ETag // was updated by Upload routine
});
return;
}
catch (StorageException ex)
when (ex.RequestInformation.HttpStatusCode == 409 || ex.RequestInformation.HttpStatusCode == 412)
{
// 409 Conflict
// This error is rare but can be thrown in very special circumstances,
// such as if the blob in the process of being created. We treat it
// as equivalent to 412 for the purposes of retry logic.
// 412 Precondition Failed
// We'll get this error if another writer updated the repository and we
// have an outdated view of its contents. If this occurs, we'll just
// refresh our view of the remote contents and try again up to the max
// retry limit.
lastError = ExceptionDispatchInfo.Capture(ex);
}
}
// if we got this far, something went awry
lastError.Throw();
}
private sealed class BlobData
{
internal byte[] BlobContents;
internal string ETag;
}
}
}

View File

@ -0,0 +1,175 @@
// 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 Microsoft.AspNetCore.DataProtection.AzureStorage;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
namespace Microsoft.AspNetCore.DataProtection
{
/// <summary>
/// Contains Azure-specific extension methods for modifying a
/// <see cref="IDataProtectionBuilder"/>.
/// </summary>
public static class AzureDataProtectionBuilderExtensions
{
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="storageAccount">The <see cref="CloudStorageAccount"/> which
/// should be utilized.</param>
/// <param name="relativePath">A relative path where the key file should be
/// stored, generally specified as "/containerName/[subDir/]keys.xml".</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="relativePath"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudStorageAccount storageAccount, string relativePath)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (storageAccount == null)
{
throw new ArgumentNullException(nameof(storageAccount));
}
if (relativePath == null)
{
throw new ArgumentNullException(nameof(relativePath));
}
// Simply concatenate the root storage endpoint with the relative path,
// which includes the container name and blob name.
var uriBuilder = new UriBuilder(storageAccount.BlobEndpoint);
uriBuilder.Path = uriBuilder.Path.TrimEnd('/') + "/" + relativePath.TrimStart('/');
// We can create a CloudBlockBlob from the storage URI and the creds.
var blobAbsoluteUri = uriBuilder.Uri;
var credentials = storageAccount.Credentials;
return PersistKeystoAzureBlobStorageInternal(builder, () => new CloudBlockBlob(blobAbsoluteUri, credentials));
}
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="blobUri">The full URI where the key file should be stored.
/// The URI must contain the SAS token as a query string parameter.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="blobUri"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, Uri blobUri)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (blobUri == null)
{
throw new ArgumentNullException(nameof(blobUri));
}
var uriBuilder = new UriBuilder(blobUri);
// The SAS token is present in the query string.
if (string.IsNullOrEmpty(uriBuilder.Query))
{
throw new ArgumentException(
message: "URI does not have a SAS token in the query string.",
paramName: nameof(blobUri));
}
var credentials = new StorageCredentials(uriBuilder.Query);
uriBuilder.Query = null; // no longer needed
var blobAbsoluteUri = uriBuilder.Uri;
return PersistKeystoAzureBlobStorageInternal(builder, () => new CloudBlockBlob(blobAbsoluteUri, credentials));
}
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="blobReference">The <see cref="CloudBlockBlob"/> where the
/// key file should be stored.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="blobReference"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudBlockBlob blobReference)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (blobReference == null)
{
throw new ArgumentNullException(nameof(blobReference));
}
// We're basically just going to make a copy of this blob.
// Use (container, blobName) instead of (storageuri, creds) since the container
// is tied to an existing service client, which contains user-settable defaults
// like retry policy and secondary connection URIs.
var container = blobReference.Container;
var blobName = blobReference.Name;
return PersistKeystoAzureBlobStorageInternal(builder, () => container.GetBlockBlobReference(blobName));
}
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="container">The <see cref="CloudBlobContainer"/> in which the
/// key file should be stored.</param>
/// <param name="blobName">The name of the key file, generally specified
/// as "[subdir/]keys.xml"</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="container"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudBlobContainer container, string blobName)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
if (blobName == null)
{
throw new ArgumentNullException(nameof(blobName));
}
return PersistKeystoAzureBlobStorageInternal(builder, () => container.GetBlockBlobReference(blobName));
}
// important: the Func passed into this method must return a new instance with each call
private static IDataProtectionBuilder PersistKeystoAzureBlobStorageInternal(IDataProtectionBuilder builder, Func<CloudBlockBlob> blobRefFactory)
{
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.XmlRepository = new AzureBlobXmlRepository(blobRefFactory);
});
return builder;
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Microsoft Azure Blob storrage support as key store.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection;azure;blob</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="WindowsAzure.Storage" Version="$(WindowsAzureStoragePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,156 @@
{
"AssemblyIdentity": "Microsoft.AspNetCore.DataProtection.AzureStorage, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": [
{
"Name": "Microsoft.AspNetCore.DataProtection.AzureDataProtectionBuilderExtensions",
"Visibility": "Public",
"Kind": "Class",
"Abstract": true,
"Static": true,
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "PersistKeysToAzureBlobStorage",
"Parameters": [
{
"Name": "builder",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder"
},
{
"Name": "storageAccount",
"Type": "Microsoft.WindowsAzure.Storage.CloudStorageAccount"
},
{
"Name": "relativePath",
"Type": "System.String"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "PersistKeysToAzureBlobStorage",
"Parameters": [
{
"Name": "builder",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder"
},
{
"Name": "blobUri",
"Type": "System.Uri"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "PersistKeysToAzureBlobStorage",
"Parameters": [
{
"Name": "builder",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder"
},
{
"Name": "blobReference",
"Type": "Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "PersistKeysToAzureBlobStorage",
"Parameters": [
{
"Name": "builder",
"Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder"
},
{
"Name": "container",
"Type": "Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer"
},
{
"Name": "blobName",
"Type": "System.String"
}
],
"ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.DataProtection.AzureStorage.AzureBlobXmlRepository",
"Visibility": "Public",
"Kind": "Class",
"Sealed": true,
"ImplementedInterfaces": [
"Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository"
],
"Members": [
{
"Kind": "Method",
"Name": "GetAllElements",
"Parameters": [],
"ReturnType": "System.Collections.Generic.IReadOnlyCollection<System.Xml.Linq.XElement>",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "StoreElement",
"Parameters": [
{
"Name": "element",
"Type": "System.Xml.Linq.XElement"
},
{
"Name": "friendlyName",
"Type": "System.String"
}
],
"ReturnType": "System.Void",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "blobRefFactory",
"Type": "System.Func<Microsoft.WindowsAzure.Storage.Blob.ICloudBlob>"
}
],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
}
]
}

Some files were not shown because too many files have changed in this diff Show More