Merge aspnet/DataProtection release/2.2 into this repo
This commit is contained in:
commit
489a88d21e
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
15
run.ps1
|
|
@ -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
17
run.sh
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@ECHO OFF
|
||||
SET RepoRoot="%~dp0..\.."
|
||||
%RepoRoot%\build.cmd -LockFile %RepoRoot%\korebuild-lock.txt -Path %~dp0 %*
|
||||
|
|
@ -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" "$@"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- TODO: temporary while we reorganize source code and refactor dependency management -->
|
||||
<DisablePackageReferenceRestrictions>true</DisablePackageReferenceRestrictions>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"CertificateThumbprint": "",
|
||||
"KeyId": "",
|
||||
"ClientId": ""
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<Import Project="..\Directory.Build.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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")]
|
||||
86
src/DataProtection/src/Microsoft.AspNetCore.Cryptography.Internal/Properties/Resources.Designer.cs
generated
Normal file
86
src/DataProtection/src/Microsoft.AspNetCore.Cryptography.Internal/Properties/Resources.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"AssemblyIdentity": "Microsoft.AspNetCore.Cryptography.Internal, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||
"Types": []
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")]
|
||||
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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")]
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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")]
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
Loading…
Reference in New Issue