[Fixes #134] Refactored DI support
- Refactored builder extensions and service collection extensions - Refactored Settings/Configuration/Descriptor - Removed ConfigurationCommon/AuthenticatedEncryptorConfigurationExtensions - Added IAuthenticatedEncryptorFactory and implementations - Refactored IKey to have Descriptor instead of CreateEncryptorInstance() - Handled Repository/Encryptor special logic - Added samples - Updated tests
This commit is contained in:
parent
bf7a238b85
commit
cde3b96aa7
|
|
@ -27,4 +27,5 @@ nuget.exe
|
|||
project.lock.json
|
||||
.vs
|
||||
.build/
|
||||
.testPublish/
|
||||
.testPublish/
|
||||
samples/**/temp-keys/
|
||||
|
|
@ -1,51 +1,57 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26127.0
|
||||
VisualStudioVersion = 15.0.26206.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection", "src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj", "{1E570CD4-6F12-44F4-961E-005EE2002BC2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{60336AB3-948D-4D15-A5FB-F32A2B91E814}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Test", "test\Microsoft.AspNetCore.DataProtection.Test\Microsoft.AspNetCore.DataProtection.Test.csproj", "{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.Internal", "src\Microsoft.AspNetCore.Cryptography.Internal\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{E2779976-A28C-4365-A4BB-4AD854FAF23E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation", "src\Microsoft.AspNetCore.Cryptography.KeyDerivation\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj", "{421F0383-34B1-402D-807B-A94542513ABA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation.Test", "test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test.csproj", "{42C97F52-8D56-46BD-A712-4F22BED157A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.Internal.Test", "test\Microsoft.AspNetCore.Cryptography.Internal.Test\Microsoft.AspNetCore.Cryptography.Internal.Test.csproj", "{37053D5F-5B61-47CE-8B72-298CE007FFB0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "src\Microsoft.AspNetCore.DataProtection.Abstractions\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{4B115BDE-B253-46A6-97BF-A8B37B344FF2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Abstractions.Test", "test\Microsoft.AspNetCore.DataProtection.Abstractions.Test\Microsoft.AspNetCore.DataProtection.Abstractions.Test.csproj", "{FF650A69-DEE4-4B36-9E30-264EE7CFB478}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.SystemWeb", "src\Microsoft.AspNetCore.DataProtection.SystemWeb\Microsoft.AspNetCore.DataProtection.SystemWeb.csproj", "{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Extensions.Test", "test\Microsoft.AspNetCore.DataProtection.Extensions.Test\Microsoft.AspNetCore.DataProtection.Extensions.Test.csproj", "{04AA8E60-A053-4D50-89FE-E76C3DF45200}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Extensions", "src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj", "{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Redis", "src\Microsoft.AspNetCore.DataProtection.Redis\Microsoft.AspNetCore.DataProtection.Redis.csproj", "{0508ADB0-9D2E-4506-9AA3-C15D7BEAE7C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.AzureStorage", "src\Microsoft.AspNetCore.DataProtection.AzureStorage\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj", "{CC799B57-81E2-4F45-8A32-0D5F49753C3F}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5A3A5DE3-49AD-431C-971D-B01B62D94AE2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureBlob", "samples\AzureBlob\AzureBlob.csproj", "{B07435B3-CD81-4E3B-88A5-6384821E1C01}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Redis.Test", "test\Microsoft.AspNetCore.DataProtection.Redis.Test\Microsoft.AspNetCore.DataProtection.Redis.Test.csproj", "{ABCF00E5-5B2F-469C-90DC-908C5A04C08D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E1D86B1B-41D8-43C9-97FD-C2BF65C414E2}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
NuGet.config = NuGet.config
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.AzureStorage.Test", "test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test.csproj", "{8C41240E-48F8-402F-9388-74CFE27F4D76}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection", "src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj", "{1E570CD4-6F12-44F4-961E-005EE2002BC2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Redis", "samples\Redis\Redis.csproj", "{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}"
|
||||
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.Redis", "src\Microsoft.AspNetCore.DataProtection.Redis\Microsoft.AspNetCore.DataProtection.Redis.csproj", "{0508ADB0-9D2E-4506-9AA3-C15D7BEAE7C9}"
|
||||
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.Redis.Test", "test\Microsoft.AspNetCore.DataProtection.Redis.Test\Microsoft.AspNetCore.DataProtection.Redis.Test.csproj", "{ABCF00E5-5B2F-469C-90DC-908C5A04C08D}"
|
||||
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
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -187,6 +193,30 @@ Global
|
|||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -209,5 +239,8 @@ Global
|
|||
{ABCF00E5-5B2F-469C-90DC-908C5A04C08D} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
|
||||
{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}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -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.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp1.1</TargetFrameworks>
|
||||
<!-- TODO remove when https://github.com/dotnet/sdk/issues/396 is resolved -->
|
||||
<RuntimeIdentifier Condition=" '$(TargetFramework)' != 'netcoreapp1.1' ">win7-x64</RuntimeIdentifier>
|
||||
<DebugType>portable</DebugType>
|
||||
<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" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.2.0-*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.2.0-*" />
|
||||
</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,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.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");
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddLogging();
|
||||
serviceCollection.AddDataProtection()
|
||||
.PersistKeysToFileSystem(new DirectoryInfo(keysFolder))
|
||||
.UseXmlEncryptor(s => new CustomXmlEncryptor(s));
|
||||
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
loggerFactory.AddConsole();
|
||||
|
||||
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,22 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:1398/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"CustomEncryptorSample": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp1.1</TargetFrameworks>
|
||||
<!-- TODO remove when https://github.com/dotnet/sdk/issues/396 is resolved -->
|
||||
<RuntimeIdentifier Condition=" '$(TargetFramework)' != 'netcoreapp1.1' ">win7-x64</RuntimeIdentifier>
|
||||
<DebugType>portable</DebugType>
|
||||
<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,64 @@
|
|||
// 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();
|
||||
}
|
||||
|
||||
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,22 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:1396/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"KeyManagementSample": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp1.1</TargetFrameworks>
|
||||
<!-- TODO remove when https://github.com/dotnet/sdk/issues/396 is resolved -->
|
||||
<RuntimeIdentifier Condition=" '$(TargetFramework)' != 'netcoreapp1.1' ">win7-x64</RuntimeIdentifier>
|
||||
<DebugType>portable</DebugType>
|
||||
<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,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,22 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:1394/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"NonDISample": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
@ -6,103 +6,90 @@ using System.Security.Cryptography;
|
|||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for configuring authenticated encryption algorithms.
|
||||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
|
||||
public sealed class AuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The algorithm to use for symmetric encryption (confidentiality).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is <see cref="EncryptionAlgorithm.AES_256_CBC"/>.
|
||||
/// </remarks>
|
||||
public EncryptionAlgorithm EncryptionAlgorithm { get; set; } = EncryptionAlgorithm.AES_256_CBC;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The algorithm to use for message authentication (tamper-proofing).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is <see cref="ValidationAlgorithm.HMACSHA256"/>.
|
||||
/// This property is ignored if <see cref="EncryptionAlgorithm"/> specifies a 'GCM' algorithm.
|
||||
/// </remarks>
|
||||
public ValidationAlgorithm ValidationAlgorithm { get; set; } = ValidationAlgorithm.HMACSHA256;
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="AuthenticatedEncryptionSettings"/> is well-formed, i.e.,
|
||||
/// that the specified algorithms actually exist and that they can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory)
|
||||
{
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8));
|
||||
try
|
||||
{
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
finally
|
||||
{
|
||||
(encryptor as IDisposable)?.Dispose();
|
||||
}
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* HELPER ROUTINES
|
||||
*/
|
||||
|
||||
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, IServiceProvider services = null)
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance(IKey key)
|
||||
{
|
||||
return CreateImplementationOptions()
|
||||
.ToConfiguration(services)
|
||||
.CreateDescriptorFromSecret(secret)
|
||||
.CreateEncryptorInstance();
|
||||
var descriptor = key.Descriptor as AuthenticatedEncryptorDescriptor;
|
||||
if (descriptor == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateAuthenticatedEncryptorInstance(descriptor.MasterKey, descriptor.Configuration);
|
||||
}
|
||||
|
||||
private IInternalAuthenticatedEncryptionSettings CreateImplementationOptions()
|
||||
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(
|
||||
ISecret secret,
|
||||
AuthenticatedEncryptorConfiguration authenticatedConfiguration)
|
||||
{
|
||||
if (IsGcmAlgorithm(EncryptionAlgorithm))
|
||||
if (authenticatedConfiguration == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm))
|
||||
{
|
||||
// GCM requires CNG, and CNG is only supported on Windows.
|
||||
if (!OSVersionUtil.IsWindows())
|
||||
{
|
||||
throw new PlatformNotSupportedException(Resources.Platform_WindowsRequiredForGcm);
|
||||
}
|
||||
return new CngGcmAuthenticatedEncryptionSettings()
|
||||
|
||||
var configuration = new CngGcmAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm)
|
||||
EncryptionAlgorithm = GetBCryptAlgorithmNameFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm)
|
||||
};
|
||||
|
||||
return new CngGcmAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// CNG preferred over managed implementations if running on Windows
|
||||
return new CngCbcAuthenticatedEncryptionSettings()
|
||||
var configuration = new CngCbcAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm),
|
||||
HashAlgorithm = GetBCryptAlgorithmName(ValidationAlgorithm)
|
||||
EncryptionAlgorithm = GetBCryptAlgorithmNameFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm),
|
||||
HashAlgorithm = GetBCryptAlgorithmNameFromValidationAlgorithm(authenticatedConfiguration.ValidationAlgorithm)
|
||||
};
|
||||
|
||||
return new CngCbcAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use managed implementations as a fallback
|
||||
return new ManagedAuthenticatedEncryptionSettings()
|
||||
var configuration = new ManagedAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithmType = GetManagedTypeForAlgorithm(EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm),
|
||||
ValidationAlgorithmType = GetManagedTypeForAlgorithm(ValidationAlgorithm)
|
||||
EncryptionAlgorithmType = GetManagedTypeFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm),
|
||||
ValidationAlgorithmType = GetManagedTypeFromValidationAlgorithm(authenticatedConfiguration.ValidationAlgorithm)
|
||||
};
|
||||
|
||||
return new ManagedAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsGcmAlgorithm(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
return (EncryptionAlgorithm.AES_128_GCM <= algorithm && algorithm <= EncryptionAlgorithm.AES_256_GCM);
|
||||
}
|
||||
|
||||
private static int GetAlgorithmKeySizeInBits(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
|
|
@ -120,11 +107,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
|||
return 256;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
throw new ArgumentOutOfRangeException(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBCryptAlgorithmName(EncryptionAlgorithm algorithm)
|
||||
private static string GetBCryptAlgorithmNameFromEncryptionAlgorithm(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
|
|
@ -137,11 +124,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
|||
return Constants.BCRYPT_AES_ALGORITHM;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
throw new ArgumentOutOfRangeException(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBCryptAlgorithmName(ValidationAlgorithm algorithm)
|
||||
private static string GetBCryptAlgorithmNameFromValidationAlgorithm(ValidationAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
|
|
@ -152,11 +139,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
|||
return Constants.BCRYPT_SHA512_ALGORITHM;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
throw new ArgumentOutOfRangeException(nameof(ValidationAlgorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static Type GetManagedTypeForAlgorithm(EncryptionAlgorithm algorithm)
|
||||
private static Type GetManagedTypeFromEncryptionAlgorithm(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
|
|
@ -169,11 +156,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
|||
return typeof(Aes);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
throw new ArgumentOutOfRangeException(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static Type GetManagedTypeForAlgorithm(ValidationAlgorithm algorithm)
|
||||
private static Type GetManagedTypeFromValidationAlgorithm(ValidationAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
|
|
@ -184,18 +171,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
|||
return typeof(HMACSHA512);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
throw new ArgumentOutOfRangeException(nameof(ValidationAlgorithm));
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsGcmAlgorithm(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
return (EncryptionAlgorithm.AES_128_GCM <= algorithm && algorithm <= EncryptionAlgorithm.AES_256_GCM);
|
||||
}
|
||||
|
||||
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionSettings.ToConfiguration(IServiceProvider services)
|
||||
{
|
||||
return new AuthenticatedEncryptorConfiguration(this, services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for configuring an authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in CBC encryption + HMAC authentication modes.
|
||||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for symmetric encryption.
|
||||
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support CBC-style encryption and must have a block size of 64 bits
|
||||
/// or greater.
|
||||
/// The default value is 'AES'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the provider which contains the implementation of the symmetric encryption algorithm.
|
||||
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in bits) of the key that will be used for symmetric encryption.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for hashing data.
|
||||
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support being opened in HMAC mode and must have a digest length
|
||||
/// of 128 bits or greater.
|
||||
/// The default value is 'SHA256'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string HashAlgorithm { get; set; } = Constants.BCRYPT_SHA256_ALGORITHM;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the provider which contains the implementation of the hash algorithm.
|
||||
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string HashAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="CngCbcAuthenticatedEncryptionSettings"/> is well-formed, i.e.,
|
||||
/// that the specified algorithms actually exist and that they can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8)))
|
||||
{
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HELPER ROUTINES
|
||||
*/
|
||||
|
||||
internal CbcAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, ILogger logger = null)
|
||||
{
|
||||
return new CbcAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(logger),
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(EncryptionAlgorithmKeySize / 8),
|
||||
hmacAlgorithmHandle: GetHmacAlgorithmHandle(logger));
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetHmacAlgorithmHandle(ILogger logger)
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(HashAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(HashAlgorithm));
|
||||
}
|
||||
|
||||
logger?.OpeningCNGAlgorithmFromProviderWithHMAC(HashAlgorithm, HashAlgorithmProvider);
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
// Special-case cached providers
|
||||
if (HashAlgorithmProvider == null)
|
||||
{
|
||||
if (HashAlgorithm == Constants.BCRYPT_SHA1_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.HMAC_SHA1; }
|
||||
else if (HashAlgorithm == Constants.BCRYPT_SHA256_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.HMAC_SHA256; }
|
||||
else if (HashAlgorithm == Constants.BCRYPT_SHA512_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.HMAC_SHA512; }
|
||||
}
|
||||
|
||||
// Look up the provider dynamically if we couldn't fetch a cached instance
|
||||
if (algorithmHandle == null)
|
||||
{
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(HashAlgorithm, HashAlgorithmProvider, hmac: true);
|
||||
}
|
||||
|
||||
// Make sure we're using a hash algorithm. We require a minimum 128-bit digest.
|
||||
var digestSize = algorithmHandle.GetHashDigestLength();
|
||||
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(digestSize * 8));
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle(ILogger logger)
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(EncryptionAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
if (EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
logger?.OpeningCNGAlgorithmFromProviderWithChainingModeCBC(EncryptionAlgorithm, EncryptionAlgorithmProvider);
|
||||
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
// Special-case cached providers
|
||||
if (EncryptionAlgorithmProvider == null)
|
||||
{
|
||||
if (EncryptionAlgorithm == Constants.BCRYPT_AES_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.AES_CBC; }
|
||||
}
|
||||
|
||||
// Look up the provider dynamically if we couldn't fetch a cached instance
|
||||
if (algorithmHandle == null)
|
||||
{
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(EncryptionAlgorithm, EncryptionAlgorithmProvider);
|
||||
algorithmHandle.SetChainingMode(Constants.BCRYPT_CHAIN_MODE_CBC);
|
||||
}
|
||||
|
||||
// make sure we're using a block cipher with an appropriate key size & block size
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmBlockSize(checked(algorithmHandle.GetCipherBlockLength() * 8));
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked((uint)EncryptionAlgorithmKeySize));
|
||||
|
||||
// make sure the provided key length is valid
|
||||
algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength((uint)EncryptionAlgorithmKeySize);
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionSettings.ToConfiguration(IServiceProvider services)
|
||||
{
|
||||
return new CngCbcAuthenticatedEncryptorConfiguration(this, services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public sealed class CngCbcAuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorFactory(ILoggerFactory loggerFactory)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<CngCbcAuthenticatedEncryptorFactory>();
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance(IKey key)
|
||||
{
|
||||
var descriptor = key.Descriptor as CngCbcAuthenticatedEncryptorDescriptor;
|
||||
if (descriptor == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateAuthenticatedEncryptorInstance(descriptor.MasterKey, descriptor.Configuration);
|
||||
}
|
||||
|
||||
internal CbcAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(
|
||||
ISecret secret,
|
||||
CngCbcAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CbcAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(configuration),
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(configuration.EncryptionAlgorithmKeySize / 8),
|
||||
hmacAlgorithmHandle: GetHmacAlgorithmHandle(configuration));
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetHmacAlgorithmHandle(CngCbcAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(configuration.HashAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(configuration.HashAlgorithm));
|
||||
}
|
||||
|
||||
_logger.OpeningCNGAlgorithmFromProviderWithHMAC(configuration.HashAlgorithm, configuration.HashAlgorithmProvider);
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
// Special-case cached providers
|
||||
if (configuration.HashAlgorithmProvider == null)
|
||||
{
|
||||
if (configuration.HashAlgorithm == Constants.BCRYPT_SHA1_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.HMAC_SHA1; }
|
||||
else if (configuration.HashAlgorithm == Constants.BCRYPT_SHA256_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.HMAC_SHA256; }
|
||||
else if (configuration.HashAlgorithm == Constants.BCRYPT_SHA512_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.HMAC_SHA512; }
|
||||
}
|
||||
|
||||
// Look up the provider dynamically if we couldn't fetch a cached instance
|
||||
if (algorithmHandle == null)
|
||||
{
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(configuration.HashAlgorithm, configuration.HashAlgorithmProvider, hmac: true);
|
||||
}
|
||||
|
||||
// Make sure we're using a hash algorithm. We require a minimum 128-bit digest.
|
||||
uint digestSize = algorithmHandle.GetHashDigestLength();
|
||||
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(digestSize * 8));
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle(CngCbcAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(configuration.EncryptionAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
if (configuration.EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(configuration.EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
_logger.OpeningCNGAlgorithmFromProviderWithChainingModeCBC(configuration.EncryptionAlgorithm, configuration.EncryptionAlgorithmProvider);
|
||||
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
// Special-case cached providers
|
||||
if (configuration.EncryptionAlgorithmProvider == null)
|
||||
{
|
||||
if (configuration.EncryptionAlgorithm == Constants.BCRYPT_AES_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.AES_CBC; }
|
||||
}
|
||||
|
||||
// Look up the provider dynamically if we couldn't fetch a cached instance
|
||||
if (algorithmHandle == null)
|
||||
{
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(configuration.EncryptionAlgorithm, configuration.EncryptionAlgorithmProvider);
|
||||
algorithmHandle.SetChainingMode(Constants.BCRYPT_CHAIN_MODE_CBC);
|
||||
}
|
||||
|
||||
// make sure we're using a block cipher with an appropriate key size & block size
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmBlockSize(checked(algorithmHandle.GetCipherBlockLength() * 8));
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked((uint)configuration.EncryptionAlgorithmKeySize));
|
||||
|
||||
// make sure the provided key length is valid
|
||||
algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength((uint)configuration.EncryptionAlgorithmKeySize);
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for configuring an authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in GCM encryption + authentication modes.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for symmetric encryption.
|
||||
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support CBC-style encryption and must have a block size exactly
|
||||
/// 128 bits.
|
||||
/// The default value is 'AES'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the provider which contains the implementation of the symmetric encryption algorithm.
|
||||
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in bits) of the key that will be used for symmetric encryption.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="CngGcmAuthenticatedEncryptionSettings"/> is well-formed, i.e.,
|
||||
/// that the specified algorithm actually exists and can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8)))
|
||||
{
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HELPER ROUTINES
|
||||
*/
|
||||
|
||||
internal GcmAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, ILogger logger = null)
|
||||
{
|
||||
return new GcmAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(logger),
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(EncryptionAlgorithmKeySize / 8));
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle(ILogger logger)
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(EncryptionAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
if (EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
logger?.OpeningCNGAlgorithmFromProviderWithChainingModeGCM(EncryptionAlgorithm, EncryptionAlgorithmProvider);
|
||||
// Special-case cached providers
|
||||
if (EncryptionAlgorithmProvider == null)
|
||||
{
|
||||
if (EncryptionAlgorithm == Constants.BCRYPT_AES_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.AES_GCM; }
|
||||
}
|
||||
|
||||
// Look up the provider dynamically if we couldn't fetch a cached instance
|
||||
if (algorithmHandle == null)
|
||||
{
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(EncryptionAlgorithm, EncryptionAlgorithmProvider);
|
||||
algorithmHandle.SetChainingMode(Constants.BCRYPT_CHAIN_MODE_GCM);
|
||||
}
|
||||
|
||||
// make sure we're using a block cipher with an appropriate key size & block size
|
||||
CryptoUtil.Assert(algorithmHandle.GetCipherBlockLength() == 128 / 8, "GCM requires a block cipher algorithm with a 128-bit block size.");
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked((uint)EncryptionAlgorithmKeySize));
|
||||
|
||||
// make sure the provided key length is valid
|
||||
algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength((uint)EncryptionAlgorithmKeySize);
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionSettings.ToConfiguration(IServiceProvider services)
|
||||
{
|
||||
return new CngGcmAuthenticatedEncryptorConfiguration(this, services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public sealed class CngGcmAuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorFactory(ILoggerFactory loggerFactory)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<CngGcmAuthenticatedEncryptorFactory>();
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance(IKey key)
|
||||
{
|
||||
var descriptor = key.Descriptor as CngGcmAuthenticatedEncryptorDescriptor;
|
||||
if (descriptor == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateAuthenticatedEncryptorInstance(descriptor.MasterKey, descriptor.Configuration);
|
||||
}
|
||||
|
||||
internal GcmAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(
|
||||
ISecret secret,
|
||||
CngGcmAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GcmAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(configuration),
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(configuration.EncryptionAlgorithmKeySize / 8));
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle(CngGcmAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(configuration.EncryptionAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
if (configuration.EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(configuration.EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
_logger?.OpeningCNGAlgorithmFromProviderWithChainingModeGCM(configuration.EncryptionAlgorithm, configuration.EncryptionAlgorithmProvider);
|
||||
// Special-case cached providers
|
||||
if (configuration.EncryptionAlgorithmProvider == null)
|
||||
{
|
||||
if (configuration.EncryptionAlgorithm == Constants.BCRYPT_AES_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.AES_GCM; }
|
||||
}
|
||||
|
||||
// Look up the provider dynamically if we couldn't fetch a cached instance
|
||||
if (algorithmHandle == null)
|
||||
{
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(configuration.EncryptionAlgorithm, configuration.EncryptionAlgorithmProvider);
|
||||
algorithmHandle.SetChainingMode(Constants.BCRYPT_CHAIN_MODE_GCM);
|
||||
}
|
||||
|
||||
// make sure we're using a block cipher with an appropriate key size & block size
|
||||
CryptoUtil.Assert(algorithmHandle.GetCipherBlockLength() == 128 / 8, "GCM requires a block cipher algorithm with a 128-bit block size.");
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked((uint)configuration.EncryptionAlgorithmKeySize));
|
||||
|
||||
// make sure the provided key length is valid
|
||||
algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength((uint)configuration.EncryptionAlgorithmKeySize);
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The basic configuration that serves as a factory for types related to authenticated encryption.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorConfiguration
|
||||
public abstract class AlgorithmConfiguration
|
||||
{
|
||||
internal const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance based on this
|
||||
/// configuration. The newly-created instance contains unique key material and is distinct
|
||||
/// from all other descriptors created by the <see cref="CreateNewDescriptor"/> method.
|
||||
/// </summary>
|
||||
/// <returns>A unique <see cref="IAuthenticatedEncryptorDescriptor"/>.</returns>
|
||||
IAuthenticatedEncryptorDescriptor CreateNewDescriptor();
|
||||
public abstract IAuthenticatedEncryptorDescriptor CreateNewDescriptor();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,42 +2,57 @@
|
|||
// 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.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generalized authenticated encryption mechanism.
|
||||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
public sealed class AuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
/// <summary>
|
||||
/// The algorithm to use for symmetric encryption (confidentiality).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is <see cref="EncryptionAlgorithm.AES_256_CBC"/>.
|
||||
/// </remarks>
|
||||
public EncryptionAlgorithm EncryptionAlgorithm { get; set; } = EncryptionAlgorithm.AES_256_CBC;
|
||||
|
||||
public AuthenticatedEncryptorConfiguration(AuthenticatedEncryptionSettings settings)
|
||||
: this(settings, services: null)
|
||||
/// <summary>
|
||||
/// The algorithm to use for message authentication (tamper-proofing).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is <see cref="ValidationAlgorithm.HMACSHA256"/>.
|
||||
/// This property is ignored if <see cref="EncryptionAlgorithm"/> specifies a 'GCM' algorithm.
|
||||
/// </remarks>
|
||||
public ValidationAlgorithm ValidationAlgorithm { get; set; } = ValidationAlgorithm.HMACSHA256;
|
||||
|
||||
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
|
||||
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
|
||||
}
|
||||
|
||||
public AuthenticatedEncryptorConfiguration(AuthenticatedEncryptionSettings settings, IServiceProvider services)
|
||||
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
if (settings == null)
|
||||
return new AuthenticatedEncryptorDescriptor(this, secret);
|
||||
}
|
||||
|
||||
void IInternalAlgorithmConfiguration.Validate()
|
||||
{
|
||||
var factory = new AuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this);
|
||||
try
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
finally
|
||||
{
|
||||
(encryptor as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public AuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
return this.CreateNewDescriptorCore();
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new AuthenticatedEncryptorDescriptor(Settings, secret, _services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,22 +8,15 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="AuthenticatedEncryptionSettings"/> object.
|
||||
/// configuration provided by an <see cref="AuthenticatedEncryptorConfiguration"/> object.
|
||||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public AuthenticatedEncryptorDescriptor(AuthenticatedEncryptionSettings settings, ISecret masterKey)
|
||||
: this(settings, masterKey, services: null)
|
||||
public AuthenticatedEncryptorDescriptor(AuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
|
||||
{
|
||||
}
|
||||
|
||||
public AuthenticatedEncryptorDescriptor(AuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
|
||||
{
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (masterKey == null)
|
||||
|
|
@ -31,19 +24,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
throw new ArgumentNullException(nameof(masterKey));
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
Configuration = configuration;
|
||||
MasterKey = masterKey;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal AuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _services);
|
||||
}
|
||||
internal AuthenticatedEncryptorConfiguration Configuration { get; }
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
|
|
@ -54,12 +41,12 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", Settings.EncryptionAlgorithm));
|
||||
new XAttribute("algorithm", Configuration.EncryptionAlgorithm));
|
||||
|
||||
var validationElement = (AuthenticatedEncryptionSettings.IsGcmAlgorithm(Settings.EncryptionAlgorithm))
|
||||
var validationElement = (AuthenticatedEncryptorFactory.IsGcmAlgorithm(Configuration.EncryptionAlgorithm))
|
||||
? (object)new XComment(" AES-GCM includes a 128-bit authentication tag, no extra validation algorithm required. ")
|
||||
: (object)new XElement("validation",
|
||||
new XAttribute("algorithm", Settings.ValidationAlgorithm));
|
||||
new XAttribute("algorithm", Configuration.ValidationAlgorithm));
|
||||
|
||||
var outerElement = new XElement("descriptor",
|
||||
encryptionElement,
|
||||
|
|
|
|||
|
|
@ -13,18 +13,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public AuthenticatedEncryptorDescriptorDeserializer()
|
||||
: this(services: null)
|
||||
{
|
||||
}
|
||||
|
||||
public AuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the <see cref="AuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
|
|
@ -41,20 +29,20 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// <masterKey requiresEncryption="true">...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var settings = new AuthenticatedEncryptionSettings();
|
||||
var configuration = new AuthenticatedEncryptorConfiguration();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
settings.EncryptionAlgorithm = (EncryptionAlgorithm)Enum.Parse(typeof(EncryptionAlgorithm), (string)encryptionElement.Attribute("algorithm"));
|
||||
configuration.EncryptionAlgorithm = (EncryptionAlgorithm)Enum.Parse(typeof(EncryptionAlgorithm), (string)encryptionElement.Attribute("algorithm"));
|
||||
|
||||
// only read <validation> if not GCM
|
||||
if (!AuthenticatedEncryptionSettings.IsGcmAlgorithm(settings.EncryptionAlgorithm))
|
||||
if (!AuthenticatedEncryptorFactory.IsGcmAlgorithm(configuration.EncryptionAlgorithm))
|
||||
{
|
||||
var validationElement = element.Element("validation");
|
||||
settings.ValidationAlgorithm = (ValidationAlgorithm)Enum.Parse(typeof(ValidationAlgorithm), (string)validationElement.Attribute("algorithm"));
|
||||
configuration.ValidationAlgorithm = (ValidationAlgorithm)Enum.Parse(typeof(ValidationAlgorithm), (string)validationElement.Attribute("algorithm"));
|
||||
}
|
||||
|
||||
Secret masterKey = ((string)element.Elements("masterKey").Single()).ToSecret();
|
||||
return new AuthenticatedEncryptorDescriptor(settings, masterKey, _services);
|
||||
return new AuthenticatedEncryptorDescriptor(configuration, masterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +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;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
|
|
@ -9,36 +9,91 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// Represents a configured authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in CBC encryption + HMAC authentication modes.
|
||||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
public sealed class CngCbcAuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for symmetric encryption.
|
||||
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support CBC-style encryption and must have a block size of 64 bits
|
||||
/// or greater.
|
||||
/// The default value is 'AES'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorConfiguration(CngCbcAuthenticatedEncryptionSettings settings)
|
||||
: this(settings, services: null)
|
||||
/// <summary>
|
||||
/// The name of the provider which contains the implementation of the symmetric encryption algorithm.
|
||||
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in bits) of the key that will be used for symmetric encryption.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for hashing data.
|
||||
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support being opened in HMAC mode and must have a digest length
|
||||
/// of 128 bits or greater.
|
||||
/// The default value is 'SHA256'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string HashAlgorithm { get; set; } = Constants.BCRYPT_SHA256_ALGORITHM;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the provider which contains the implementation of the hash algorithm.
|
||||
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string HashAlgorithmProvider { get; set; } = null;
|
||||
|
||||
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
|
||||
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
|
||||
}
|
||||
|
||||
public CngCbcAuthenticatedEncryptorConfiguration(CngCbcAuthenticatedEncryptionSettings settings, IServiceProvider services)
|
||||
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
if (settings == null)
|
||||
return new CngCbcAuthenticatedEncryptorDescriptor(this, secret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="CngCbcAuthenticatedEncryptorConfiguration"/> is well-formed, i.e.,
|
||||
/// that the specified algorithms actually exist and that they can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
void IInternalAlgorithmConfiguration.Validate()
|
||||
{
|
||||
var factory = new CngCbcAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public CngCbcAuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
return this.CreateNewDescriptorCore();
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new CngCbcAuthenticatedEncryptorDescriptor(Settings, secret, _services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,28 +3,20 @@
|
|||
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="CngCbcAuthenticatedEncryptionSettings"/> object.
|
||||
/// configuration provided by an <see cref="CngCbcAuthenticatedEncryptorConfiguration"/> object.
|
||||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
private readonly ILogger _log;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorDescriptor(CngCbcAuthenticatedEncryptionSettings settings, ISecret masterKey)
|
||||
: this(settings, masterKey, services: null)
|
||||
public CngCbcAuthenticatedEncryptorDescriptor(CngCbcAuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
|
||||
{
|
||||
}
|
||||
|
||||
public CngCbcAuthenticatedEncryptorDescriptor(CngCbcAuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
|
||||
{
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (masterKey == null)
|
||||
|
|
@ -32,19 +24,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
throw new ArgumentNullException(nameof(masterKey));
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
Configuration = configuration;
|
||||
MasterKey = masterKey;
|
||||
_log = services.GetLogger<CngCbcAuthenticatedEncryptorDescriptor>();
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal CngCbcAuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _log);
|
||||
}
|
||||
internal CngCbcAuthenticatedEncryptorConfiguration Configuration { get; }
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
|
|
@ -56,18 +42,18 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", Settings.EncryptionAlgorithm),
|
||||
new XAttribute("keyLength", Settings.EncryptionAlgorithmKeySize));
|
||||
if (Settings.EncryptionAlgorithmProvider != null)
|
||||
new XAttribute("algorithm", Configuration.EncryptionAlgorithm),
|
||||
new XAttribute("keyLength", Configuration.EncryptionAlgorithmKeySize));
|
||||
if (Configuration.EncryptionAlgorithmProvider != null)
|
||||
{
|
||||
encryptionElement.SetAttributeValue("provider", Settings.EncryptionAlgorithmProvider);
|
||||
encryptionElement.SetAttributeValue("provider", Configuration.EncryptionAlgorithmProvider);
|
||||
}
|
||||
|
||||
var hashElement = new XElement("hash",
|
||||
new XAttribute("algorithm", Settings.HashAlgorithm));
|
||||
if (Settings.HashAlgorithmProvider != null)
|
||||
new XAttribute("algorithm", Configuration.HashAlgorithm));
|
||||
if (Configuration.HashAlgorithmProvider != null)
|
||||
{
|
||||
hashElement.SetAttributeValue("provider", Settings.HashAlgorithmProvider);
|
||||
hashElement.SetAttributeValue("provider", Configuration.HashAlgorithmProvider);
|
||||
}
|
||||
|
||||
var rootElement = new XElement("descriptor",
|
||||
|
|
|
|||
|
|
@ -12,18 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorDescriptorDeserializer()
|
||||
: this(services: null)
|
||||
{
|
||||
}
|
||||
|
||||
public CngCbcAuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
|
|
@ -41,20 +29,20 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var settings = new CngCbcAuthenticatedEncryptionSettings();
|
||||
var configuration = new CngCbcAuthenticatedEncryptorConfiguration();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
settings.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
settings.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
settings.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
|
||||
configuration.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
configuration.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
configuration.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
|
||||
|
||||
var hashElement = element.Element("hash");
|
||||
settings.HashAlgorithm = (string)hashElement.Attribute("algorithm");
|
||||
settings.HashAlgorithmProvider = (string)hashElement.Attribute("provider"); // could be null
|
||||
configuration.HashAlgorithm = (string)hashElement.Attribute("algorithm");
|
||||
configuration.HashAlgorithmProvider = (string)hashElement.Attribute("provider"); // could be null
|
||||
|
||||
Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
|
||||
|
||||
return new CngCbcAuthenticatedEncryptorDescriptor(settings, masterKey, _services);
|
||||
return new CngCbcAuthenticatedEncryptorDescriptor(configuration, masterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +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;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
|
|
@ -9,36 +9,67 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// Represents a configured authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in GCM encryption + authentication modes.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
public sealed class CngGcmAuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for symmetric encryption.
|
||||
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support GCM-style encryption and must have a block size exactly
|
||||
/// 128 bits.
|
||||
/// The default value is 'AES'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorConfiguration(CngGcmAuthenticatedEncryptionSettings settings)
|
||||
: this(settings, services: null)
|
||||
/// <summary>
|
||||
/// The name of the provider which contains the implementation of the symmetric encryption algorithm.
|
||||
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
|
||||
/// This property is optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in bits) of the key that will be used for symmetric encryption.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
|
||||
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
|
||||
}
|
||||
|
||||
public CngGcmAuthenticatedEncryptorConfiguration(CngGcmAuthenticatedEncryptionSettings settings, IServiceProvider services)
|
||||
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
if (settings == null)
|
||||
return new CngGcmAuthenticatedEncryptorDescriptor(this, secret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="CngGcmAuthenticatedEncryptorConfiguration"/> is well-formed, i.e.,
|
||||
/// that the specified algorithm actually exists and can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
void IInternalAlgorithmConfiguration.Validate()
|
||||
{
|
||||
var factory = new CngGcmAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public CngGcmAuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
return this.CreateNewDescriptorCore();
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new CngGcmAuthenticatedEncryptorDescriptor(Settings, secret, _services);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,22 +9,15 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="CngGcmAuthenticatedEncryptionSettings"/> object.
|
||||
/// configuration provided by an <see cref="CngGcmAuthenticatedEncryptorConfiguration"/> object.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
private readonly ILogger _log;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorDescriptor(CngGcmAuthenticatedEncryptionSettings settings, ISecret masterKey)
|
||||
: this(settings, masterKey, services: null)
|
||||
public CngGcmAuthenticatedEncryptorDescriptor(CngGcmAuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
|
||||
{
|
||||
}
|
||||
|
||||
public CngGcmAuthenticatedEncryptorDescriptor(CngGcmAuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
|
||||
{
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (masterKey == null)
|
||||
|
|
@ -32,19 +25,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
throw new ArgumentNullException(nameof(masterKey));
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
Configuration = configuration;
|
||||
MasterKey = masterKey;
|
||||
_log = services.GetLogger<CngGcmAuthenticatedEncryptorDescriptor>();
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal CngGcmAuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _log);
|
||||
}
|
||||
internal CngGcmAuthenticatedEncryptorConfiguration Configuration { get; }
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
|
|
@ -55,11 +42,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", Settings.EncryptionAlgorithm),
|
||||
new XAttribute("keyLength", Settings.EncryptionAlgorithmKeySize));
|
||||
if (Settings.EncryptionAlgorithmProvider != null)
|
||||
new XAttribute("algorithm", Configuration.EncryptionAlgorithm),
|
||||
new XAttribute("keyLength", Configuration.EncryptionAlgorithmKeySize));
|
||||
if (Configuration.EncryptionAlgorithmProvider != null)
|
||||
{
|
||||
encryptionElement.SetAttributeValue("provider", Settings.EncryptionAlgorithmProvider);
|
||||
encryptionElement.SetAttributeValue("provider", Configuration.EncryptionAlgorithmProvider);
|
||||
}
|
||||
|
||||
var rootElement = new XElement("descriptor",
|
||||
|
|
|
|||
|
|
@ -12,17 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorDescriptorDeserializer()
|
||||
: this(services: null)
|
||||
{
|
||||
}
|
||||
|
||||
public CngGcmAuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
|
|
@ -40,16 +29,16 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var settings = new CngGcmAuthenticatedEncryptionSettings();
|
||||
var configuration = new CngGcmAuthenticatedEncryptorConfiguration();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
settings.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
settings.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
settings.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
|
||||
configuration.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
configuration.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
configuration.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
|
||||
|
||||
Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
|
||||
|
||||
return new CngGcmAuthenticatedEncryptorDescriptor(settings, masterKey, _services);
|
||||
return new CngGcmAuthenticatedEncryptorDescriptor(configuration, masterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
// 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.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
internal static class ConfigurationCommon
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IAuthenticatedEncryptorDescriptor"/> from this <see cref="IInternalAuthenticatedEncryptorConfiguration"/>
|
||||
/// using a random 512-bit master key generated from a secure PRNG.
|
||||
/// </summary>
|
||||
public static IAuthenticatedEncryptorDescriptor CreateNewDescriptorCore(this IInternalAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
return configuration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,17 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IAuthenticatedEncryptor"/> instance based on the current descriptor.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IAuthenticatedEncryptor"/> instance.</returns>
|
||||
/// <remarks>
|
||||
/// For a given descriptor, any two instances returned by this method should
|
||||
/// be considered equivalent, e.g., the payload returned by one's <see cref="IAuthenticatedEncryptor.Encrypt(ArraySegment{byte}, ArraySegment{byte})"/>
|
||||
/// method should be consumable by the other's <see cref="IAuthenticatedEncryptor.Decrypt(ArraySegment{byte}, ArraySegment{byte})"/> method.
|
||||
/// </remarks>
|
||||
IAuthenticatedEncryptor CreateEncryptorInstance();
|
||||
|
||||
/// <summary>
|
||||
/// Exports the current descriptor to XML.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,27 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
// This type is not public because we don't want to lock ourselves into a contract stating
|
||||
// that a descriptor is simply a configuration plus a single serializable, reproducible secret.
|
||||
|
||||
/// <summary>
|
||||
/// A type that knows how to create instances of an <see cref="IAuthenticatedEncryptorDescriptor"/>
|
||||
/// given specific secret key material.
|
||||
/// </summary>
|
||||
internal interface IInternalAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration
|
||||
/// <remarks>
|
||||
/// This type is not public because we don't want to lock ourselves into a contract stating
|
||||
/// that a descriptor is simply a configuration plus a single serializable, reproducible secret.
|
||||
/// </remarks>
|
||||
internal interface IInternalAlgorithmConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this
|
||||
/// configuration given specific secret key material.
|
||||
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this configuration
|
||||
/// given specific secret key material.
|
||||
/// </summary>
|
||||
IAuthenticatedEncryptorDescriptor CreateDescriptorFromSecret(ISecret secret);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a self-test of the algorithm specified by the configuration object.
|
||||
/// </summary>
|
||||
void Validate();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
|
|
@ -10,36 +11,97 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// managed <see cref="System.Security.Cryptography.SymmetricAlgorithm"/> and
|
||||
/// <see cref="System.Security.Cryptography.KeyedHashAlgorithm"/> types.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
public sealed class ManagedAuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
/// <summary>
|
||||
/// The type of the algorithm to use for symmetric encryption.
|
||||
/// The type must subclass <see cref="SymmetricAlgorithm"/>.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support CBC-style encryption and PKCS#7 padding and must have a block size of 64 bits or greater.
|
||||
/// The default algorithm is AES.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public Type EncryptionAlgorithmType { get; set; } = typeof(Aes);
|
||||
|
||||
public ManagedAuthenticatedEncryptorConfiguration(ManagedAuthenticatedEncryptionSettings settings)
|
||||
: this(settings, services: null)
|
||||
/// <summary>
|
||||
/// The length (in bits) of the key that will be used for symmetric encryption.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the algorithm to use for validation.
|
||||
/// Type type must subclass <see cref="KeyedHashAlgorithm"/>.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must have a digest length of 128 bits or greater.
|
||||
/// The default algorithm is HMACSHA256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public Type ValidationAlgorithmType { get; set; } = typeof(HMACSHA256);
|
||||
|
||||
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
|
||||
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
|
||||
}
|
||||
|
||||
public ManagedAuthenticatedEncryptorConfiguration(ManagedAuthenticatedEncryptionSettings settings, IServiceProvider services)
|
||||
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
if (settings == null)
|
||||
return new ManagedAuthenticatedEncryptorDescriptor(this, secret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="ManagedAuthenticatedEncryptorConfiguration"/> is well-formed, i.e.,
|
||||
/// that the specified algorithms actually exist and can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
void IInternalAlgorithmConfiguration.Validate()
|
||||
{
|
||||
var factory = new ManagedAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public ManagedAuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
// Any changes to this method should also be be reflected
|
||||
// in ManagedAuthenticatedEncryptorDescriptorDeserializer.FriendlyNameToType.
|
||||
private static string TypeToFriendlyName(Type type)
|
||||
{
|
||||
return this.CreateNewDescriptorCore();
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptorDescriptor(Settings, secret, _services);
|
||||
if (type == typeof(Aes))
|
||||
{
|
||||
return nameof(Aes);
|
||||
}
|
||||
else if (type == typeof(HMACSHA1))
|
||||
{
|
||||
return nameof(HMACSHA1);
|
||||
}
|
||||
else if (type == typeof(HMACSHA256))
|
||||
{
|
||||
return nameof(HMACSHA256);
|
||||
}
|
||||
else if (type == typeof(HMACSHA384))
|
||||
{
|
||||
return nameof(HMACSHA384);
|
||||
}
|
||||
else if (type == typeof(HMACSHA512))
|
||||
{
|
||||
return nameof(HMACSHA512);
|
||||
}
|
||||
else
|
||||
{
|
||||
return type.AssemblyQualifiedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,28 +4,20 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="ManagedAuthenticatedEncryptionSettings"/> object.
|
||||
/// configuration provided by an <see cref="ManagedAuthenticatedEncryptorConfiguration"/> object.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
private readonly ILogger _log;
|
||||
|
||||
public ManagedAuthenticatedEncryptorDescriptor(ManagedAuthenticatedEncryptionSettings settings, ISecret masterKey)
|
||||
: this(settings, masterKey, services: null)
|
||||
public ManagedAuthenticatedEncryptorDescriptor(ManagedAuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
|
||||
{
|
||||
}
|
||||
|
||||
public ManagedAuthenticatedEncryptorDescriptor(ManagedAuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
|
||||
{
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (masterKey == null)
|
||||
|
|
@ -33,19 +25,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
throw new ArgumentNullException(nameof(masterKey));
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
Configuration = configuration;
|
||||
MasterKey = masterKey;
|
||||
_log = services.GetLogger<ManagedAuthenticatedEncryptorDescriptor>();
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal ManagedAuthenticatedEncryptionSettings Settings { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _log);
|
||||
}
|
||||
internal ManagedAuthenticatedEncryptorConfiguration Configuration { get; }
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
|
|
@ -57,11 +43,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", TypeToFriendlyName(Settings.EncryptionAlgorithmType)),
|
||||
new XAttribute("keyLength", Settings.EncryptionAlgorithmKeySize));
|
||||
new XAttribute("algorithm", TypeToFriendlyName(Configuration.EncryptionAlgorithmType)),
|
||||
new XAttribute("keyLength", Configuration.EncryptionAlgorithmKeySize));
|
||||
|
||||
var validationElement = new XElement("validation",
|
||||
new XAttribute("algorithm", TypeToFriendlyName(Settings.ValidationAlgorithmType)));
|
||||
new XAttribute("algorithm", TypeToFriendlyName(Configuration.ValidationAlgorithmType)));
|
||||
|
||||
var rootElement = new XElement("descriptor",
|
||||
new XComment(" Algorithms provided by specified SymmetricAlgorithm and KeyedHashAlgorithm "),
|
||||
|
|
|
|||
|
|
@ -13,18 +13,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public ManagedAuthenticatedEncryptorDescriptorDeserializer()
|
||||
: this(services: null)
|
||||
{
|
||||
}
|
||||
|
||||
public ManagedAuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the <see cref="ManagedAuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
|
|
@ -42,18 +30,18 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var settings = new ManagedAuthenticatedEncryptionSettings();
|
||||
var configuration = new ManagedAuthenticatedEncryptorConfiguration();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
settings.EncryptionAlgorithmType = FriendlyNameToType((string)encryptionElement.Attribute("algorithm"));
|
||||
settings.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
configuration.EncryptionAlgorithmType = FriendlyNameToType((string)encryptionElement.Attribute("algorithm"));
|
||||
configuration.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
|
||||
var validationElement = element.Element("validation");
|
||||
settings.ValidationAlgorithmType = FriendlyNameToType((string)validationElement.Attribute("algorithm"));
|
||||
configuration.ValidationAlgorithmType = FriendlyNameToType((string)validationElement.Attribute("algorithm"));
|
||||
|
||||
Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
|
||||
|
||||
return new ManagedAuthenticatedEncryptorDescriptor(settings, masterKey, _services);
|
||||
return new ManagedAuthenticatedEncryptorDescriptor(configuration, masterKey);
|
||||
}
|
||||
|
||||
// Any changes to this method should also be be reflected
|
||||
|
|
|
|||
|
|
@ -1,8 +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;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
// 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.KeyManagement;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public interface IAuthenticatedEncryptorFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IAuthenticatedEncryptor"/> instance based on the given <see cref="IKey.Descriptor"/>.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IAuthenticatedEncryptor"/> instance.</returns>
|
||||
/// <remarks>
|
||||
/// For a given <see cref="IKey.Descriptor"/>, any two instances returned by this method should
|
||||
/// be considered equivalent, e.g., the payload returned by one's <see cref="IAuthenticatedEncryptor.Encrypt(ArraySegment{byte}, ArraySegment{byte})"/>
|
||||
/// method should be consumable by the other's <see cref="IAuthenticatedEncryptor.Decrypt(ArraySegment{byte}, ArraySegment{byte})"/> method.
|
||||
/// </remarks>
|
||||
IAuthenticatedEncryptor CreateEncryptorInstance(IKey key);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// 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.AuthenticatedEncryption.ConfigurationModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Implemented by our settings classes to generalize creating configuration objects.
|
||||
/// </summary>
|
||||
internal interface IInternalAuthenticatedEncryptionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="IInternalAuthenticatedEncryptorConfiguration"/> object
|
||||
/// from the given settings.
|
||||
/// </summary>
|
||||
IInternalAuthenticatedEncryptorConfiguration ToConfiguration(IServiceProvider services);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a self-test of the algorithm specified by the settings object.
|
||||
/// </summary>
|
||||
void Validate();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
// 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.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Managed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for configuring an authenticated encryption mechanism which uses
|
||||
/// managed SymmetricAlgorithm and KeyedHashAlgorithm implementations.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the algorithm to use for symmetric encryption.
|
||||
/// The type must subclass <see cref="SymmetricAlgorithm"/>.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must support CBC-style encryption and PKCS#7 padding and must have a block size of 64 bits or greater.
|
||||
/// The default algorithm is AES.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public Type EncryptionAlgorithmType { get; set; } = typeof(Aes);
|
||||
|
||||
/// <summary>
|
||||
/// The length (in bits) of the key that will be used for symmetric encryption.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the algorithm to use for validation.
|
||||
/// Type type must subclass <see cref="KeyedHashAlgorithm"/>.
|
||||
/// This property is required to have a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The algorithm must have a digest length of 128 bits or greater.
|
||||
/// The default algorithm is HMACSHA256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public Type ValidationAlgorithmType { get; set; } = typeof(HMACSHA256);
|
||||
|
||||
/// <summary>
|
||||
/// Validates that this <see cref="ManagedAuthenticatedEncryptionSettings"/> is well-formed, i.e.,
|
||||
/// that the specified algorithms actually exist and can be instantiated properly.
|
||||
/// An exception will be thrown if validation fails.
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8)))
|
||||
{
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HELPER ROUTINES
|
||||
*/
|
||||
|
||||
internal ManagedAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, ILogger logger = null)
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmFactory: GetSymmetricBlockCipherAlgorithmFactory(logger),
|
||||
symmetricAlgorithmKeySizeInBytes: EncryptionAlgorithmKeySize / 8,
|
||||
validationAlgorithmFactory: GetKeyedHashAlgorithmFactory(logger));
|
||||
}
|
||||
|
||||
private Func<KeyedHashAlgorithm> GetKeyedHashAlgorithmFactory(ILogger logger)
|
||||
{
|
||||
// basic argument checking
|
||||
if (ValidationAlgorithmType == null)
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(ValidationAlgorithmType));
|
||||
}
|
||||
|
||||
logger?.UsingManagedKeyedHashAlgorithm(ValidationAlgorithmType.FullName);
|
||||
if (ValidationAlgorithmType == typeof(HMACSHA256))
|
||||
{
|
||||
return () => new HMACSHA256();
|
||||
}
|
||||
else if (ValidationAlgorithmType == typeof(HMACSHA512))
|
||||
{
|
||||
return () => new HMACSHA512();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AlgorithmActivator.CreateFactory<KeyedHashAlgorithm>(ValidationAlgorithmType);
|
||||
}
|
||||
}
|
||||
|
||||
private Func<SymmetricAlgorithm> GetSymmetricBlockCipherAlgorithmFactory(ILogger logger)
|
||||
{
|
||||
// basic argument checking
|
||||
if (EncryptionAlgorithmType == null)
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithmType));
|
||||
}
|
||||
typeof(SymmetricAlgorithm).AssertIsAssignableFrom(EncryptionAlgorithmType);
|
||||
if (EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
logger?.UsingManagedSymmetricAlgorithm(EncryptionAlgorithmType.FullName);
|
||||
|
||||
if (EncryptionAlgorithmType == typeof(Aes))
|
||||
{
|
||||
Func<Aes> factory = null;
|
||||
#if !NETSTANDARD1_3
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// If we're on desktop CLR and running on Windows, use the FIPS-compliant implementation.
|
||||
factory = () => new AesCryptoServiceProvider();
|
||||
}
|
||||
#endif
|
||||
return factory ?? Aes.Create;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AlgorithmActivator.CreateFactory<SymmetricAlgorithm>(EncryptionAlgorithmType);
|
||||
}
|
||||
}
|
||||
|
||||
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionSettings.ToConfiguration(IServiceProvider services)
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptorConfiguration(this, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper methods for generating cryptographic algorithm factories.
|
||||
/// </summary>
|
||||
private static class AlgorithmActivator
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a factory that wraps a call to <see cref="Activator.CreateInstance{T}"/>.
|
||||
/// </summary>
|
||||
public static Func<T> CreateFactory<T>(Type implementation)
|
||||
{
|
||||
return ((IActivator<T>)Activator.CreateInstance(typeof(AlgorithmActivatorCore<>).MakeGenericType(implementation))).Creator;
|
||||
}
|
||||
|
||||
private interface IActivator<out T>
|
||||
{
|
||||
Func<T> Creator { get; }
|
||||
}
|
||||
|
||||
private class AlgorithmActivatorCore<T> : IActivator<T> where T : new()
|
||||
{
|
||||
public Func<T> Creator { get; } = Activator.CreateInstance<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// 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.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Managed;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public sealed class ManagedAuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ManagedAuthenticatedEncryptorFactory(ILoggerFactory loggerFactory)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<ManagedAuthenticatedEncryptorFactory>();
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance(IKey key)
|
||||
{
|
||||
var descriptor = key.Descriptor as ManagedAuthenticatedEncryptorDescriptor;
|
||||
if (descriptor == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateAuthenticatedEncryptorInstance(descriptor.MasterKey, descriptor.Configuration);
|
||||
}
|
||||
|
||||
internal ManagedAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(
|
||||
ISecret secret,
|
||||
ManagedAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ManagedAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmFactory: GetSymmetricBlockCipherAlgorithmFactory(configuration),
|
||||
symmetricAlgorithmKeySizeInBytes: configuration.EncryptionAlgorithmKeySize / 8,
|
||||
validationAlgorithmFactory: GetKeyedHashAlgorithmFactory(configuration));
|
||||
}
|
||||
|
||||
private Func<KeyedHashAlgorithm> GetKeyedHashAlgorithmFactory(ManagedAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
// basic argument checking
|
||||
if (configuration.ValidationAlgorithmType == null)
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(configuration.ValidationAlgorithmType));
|
||||
}
|
||||
|
||||
_logger.UsingManagedKeyedHashAlgorithm(configuration.ValidationAlgorithmType.FullName);
|
||||
if (configuration.ValidationAlgorithmType == typeof(HMACSHA256))
|
||||
{
|
||||
return () => new HMACSHA256();
|
||||
}
|
||||
else if (configuration.ValidationAlgorithmType == typeof(HMACSHA512))
|
||||
{
|
||||
return () => new HMACSHA512();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AlgorithmActivator.CreateFactory<KeyedHashAlgorithm>(configuration.ValidationAlgorithmType);
|
||||
}
|
||||
}
|
||||
|
||||
private Func<SymmetricAlgorithm> GetSymmetricBlockCipherAlgorithmFactory(ManagedAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
// basic argument checking
|
||||
if (configuration.EncryptionAlgorithmType == null)
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(configuration.EncryptionAlgorithmType));
|
||||
}
|
||||
typeof(SymmetricAlgorithm).AssertIsAssignableFrom(configuration.EncryptionAlgorithmType);
|
||||
if (configuration.EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(configuration.EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
_logger.UsingManagedSymmetricAlgorithm(configuration.EncryptionAlgorithmType.FullName);
|
||||
|
||||
if (configuration.EncryptionAlgorithmType == typeof(Aes))
|
||||
{
|
||||
Func<Aes> factory = null;
|
||||
#if !NETSTANDARD1_3
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// If we're on desktop CLR and running on Windows, use the FIPS-compliant implementation.
|
||||
factory = () => new AesCryptoServiceProvider();
|
||||
}
|
||||
#endif
|
||||
return factory ?? Aes.Create;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AlgorithmActivator.CreateFactory<SymmetricAlgorithm>(configuration.EncryptionAlgorithmType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper methods for generating cryptographic algorithm factories.
|
||||
/// </summary>
|
||||
private static class AlgorithmActivator
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a factory that wraps a call to <see cref="Activator.CreateInstance{T}"/>.
|
||||
/// </summary>
|
||||
public static Func<T> CreateFactory<T>(Type implementation)
|
||||
{
|
||||
return ((IActivator<T>)Activator.CreateInstance(typeof(AlgorithmActivatorCore<>).MakeGenericType(implementation))).Creator;
|
||||
}
|
||||
|
||||
private interface IActivator<out T>
|
||||
{
|
||||
Func<T> Creator { get; }
|
||||
}
|
||||
|
||||
private class AlgorithmActivatorCore<T> : IActivator<T> where T : new()
|
||||
{
|
||||
public Func<T> Creator { get; } = Activator.CreateInstance<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,16 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Win32;
|
||||
|
||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
|
|
@ -68,7 +73,11 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(sink));
|
||||
}
|
||||
|
||||
builder.Services.AddSingleton<IKeyEscrowSink>(sink);
|
||||
builder.Services.Configure<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.KeyEscrowSinks.Add(sink);
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +98,15 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Services.AddSingleton<IKeyEscrowSink, TImplementation>();
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var implementationInstance = services.GetRequiredService<TImplementation>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.KeyEscrowSinks.Add(implementationInstance);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +131,15 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
builder.Services.AddSingleton<IKeyEscrowSink>(factory);
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var instance = factory(services);
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.KeyEscrowSinks.Add(instance);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +207,15 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(directory));
|
||||
}
|
||||
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IXmlRepository_FileSystem(directory));
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.XmlRepository = new FileSystemXmlRepository(directory, loggerFactory);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +237,15 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(registryKey));
|
||||
}
|
||||
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IXmlRepository_Registry(registryKey));
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.XmlRepository = new RegistryXmlRepository(registryKey, loggerFactory);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +269,15 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(certificate));
|
||||
}
|
||||
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(certificate));
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.XmlEncryptor = new CertificateXmlEncryptor(certificate, loggerFactory);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -256,12 +305,20 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw Error.CertificateXmlEncryptor_CertificateNotFound(thumbprint);
|
||||
}
|
||||
|
||||
var services = builder.Services;
|
||||
|
||||
// ICertificateResolver is necessary for this type to work correctly, so register it
|
||||
// if it doesn't already exist.
|
||||
services.TryAdd(DataProtectionServiceDescriptors.ICertificateResolver_Default());
|
||||
Use(services, DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(thumbprint));
|
||||
builder.Services.TryAddSingleton<ICertificateResolver, CertificateResolver>();
|
||||
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
var certificateResolver = services.GetRequiredService<ICertificateResolver>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.XmlEncryptor = new CertificateXmlEncryptor(thumbprint, certificateResolver, loggerFactory);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +362,16 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToLocalMachine));
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
CryptoUtil.AssertPlatformIsWindows();
|
||||
options.XmlEncryptor = new DpapiXmlEncryptor(protectToLocalMachine, loggerFactory);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +424,16 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(protectionDescriptorRule));
|
||||
}
|
||||
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IXmlEncryptor_DpapiNG(protectionDescriptorRule, flags));
|
||||
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
CryptoUtil.AssertPlatformIsWindows8OrLater();
|
||||
options.XmlEncryptor = new DpapiNGXmlEncryptor(protectionDescriptorRule, flags, loggerFactory);
|
||||
});
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -395,21 +470,21 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// by default when generating protected payloads.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
|
||||
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
|
||||
public static IDataProtectionBuilder UseCryptographicAlgorithms(this IDataProtectionBuilder builder, AuthenticatedEncryptionSettings settings)
|
||||
public static IDataProtectionBuilder UseCryptographicAlgorithms(this IDataProtectionBuilder builder, AuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
return UseCryptographicAlgorithmsCore(builder, settings);
|
||||
return UseCryptographicAlgorithmsCore(builder, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -419,25 +494,25 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// <see cref="ValidationAlgorithm"/> enumerations.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
|
||||
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
|
||||
/// <remarks>
|
||||
/// This API is only available on Windows.
|
||||
/// </remarks>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngCbcAuthenticatedEncryptionSettings settings)
|
||||
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngCbcAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
return UseCryptographicAlgorithmsCore(builder, settings);
|
||||
return UseCryptographicAlgorithmsCore(builder, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -447,25 +522,25 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// <see cref="ValidationAlgorithm"/> enumerations.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
|
||||
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
|
||||
/// <remarks>
|
||||
/// This API is only available on Windows.
|
||||
/// </remarks>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngGcmAuthenticatedEncryptionSettings settings)
|
||||
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngGcmAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
return UseCryptographicAlgorithmsCore(builder, settings);
|
||||
return UseCryptographicAlgorithmsCore(builder, configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -475,28 +550,33 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// <see cref="ValidationAlgorithm"/> enumerations.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
|
||||
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, ManagedAuthenticatedEncryptionSettings settings)
|
||||
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, ManagedAuthenticatedEncryptorConfiguration configuration)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (settings == null)
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
return UseCryptographicAlgorithmsCore(builder, settings);
|
||||
return UseCryptographicAlgorithmsCore(builder, configuration);
|
||||
}
|
||||
|
||||
private static IDataProtectionBuilder UseCryptographicAlgorithmsCore(IDataProtectionBuilder builder, IInternalAuthenticatedEncryptionSettings settings)
|
||||
private static IDataProtectionBuilder UseCryptographicAlgorithmsCore(IDataProtectionBuilder builder, AlgorithmConfiguration configuration)
|
||||
{
|
||||
settings.Validate(); // perform self-test
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(settings));
|
||||
((IInternalAlgorithmConfiguration)configuration).Validate(); // perform self-test
|
||||
|
||||
builder.Services.Configure<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.AuthenticatedEncryptorConfiguration = configuration;
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -517,30 +597,9 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
Use(builder.Services, DataProtectionServiceDescriptors.IDataProtectionProvider_Ephemeral());
|
||||
builder.Services.Replace(ServiceDescriptor.Singleton<IDataProtectionProvider, EphemeralDataProtectionProvider>());
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/*
|
||||
* UTILITY ISERVICECOLLECTION METHODS
|
||||
*/
|
||||
|
||||
private static void RemoveAllServicesOfType(IServiceCollection services, Type serviceType)
|
||||
{
|
||||
// We go backward since we're modifying the collection in-place.
|
||||
for (var i = services.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (services[i]?.ServiceType == serviceType)
|
||||
{
|
||||
services.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Use(IServiceCollection services, ServiceDescriptor descriptor)
|
||||
{
|
||||
RemoveAllServicesOfType(services, descriptor.ServiceType);
|
||||
services.Add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +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 Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains static factory methods for creating <see cref="IDataProtectionProvider"/> instances.
|
||||
/// </summary>
|
||||
internal static class DataProtectionProviderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IDataProtectionProvider"/> given an <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The global options to use when creating the provider.</param>
|
||||
/// <param name="services">Provides mandatory services for use by the provider.</param>
|
||||
/// <returns>An <see cref="IDataProtectionProvider"/>.</returns>
|
||||
public static IDataProtectionProvider GetProviderFromServices(DataProtectionOptions options, IServiceProvider services)
|
||||
public static ILoggerFactory GetDefaultLoggerFactory()
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
return GetProviderFromServices(options, services, mustCreateImmediately: false);
|
||||
}
|
||||
|
||||
internal static IDataProtectionProvider GetProviderFromServices(DataProtectionOptions options, IServiceProvider services, bool mustCreateImmediately)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
IDataProtectionProvider dataProtectionProvider = null;
|
||||
|
||||
// If we're being asked to create the provider immediately, then it means that
|
||||
// we're already in a call to GetService, and we're responsible for supplying
|
||||
// the default implementation ourselves. We can't call GetService again or
|
||||
// else we risk stack diving.
|
||||
if (!mustCreateImmediately)
|
||||
{
|
||||
dataProtectionProvider = services.GetService<IDataProtectionProvider>();
|
||||
}
|
||||
|
||||
// If all else fails, create a keyring manually based on the other registered services.
|
||||
if (dataProtectionProvider == null)
|
||||
{
|
||||
var keyRingProvider = new KeyRingProvider(
|
||||
keyManager: services.GetRequiredService<IKeyManager>(),
|
||||
keyManagementOptions: services.GetService<IOptions<KeyManagementOptions>>()?.Value, // might be null
|
||||
services: services);
|
||||
dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyRingProvider, services);
|
||||
}
|
||||
|
||||
// Finally, link the provider to the supplied discriminator
|
||||
if (!String.IsNullOrEmpty(options.ApplicationDiscriminator))
|
||||
{
|
||||
dataProtectionProvider = dataProtectionProvider.CreateProtector(options.ApplicationDiscriminator);
|
||||
}
|
||||
|
||||
return dataProtectionProvider;
|
||||
return NullLoggerFactory.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,15 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.DataProtection.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
|
|
@ -26,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
services.AddSingleton<IActivator, RC1ForwardingActivator>();
|
||||
services.AddOptions();
|
||||
services.TryAdd(DataProtectionServices.GetDefaultServices());
|
||||
AddDataProtectionServices(services);
|
||||
|
||||
return new DataProtectionBuilder(services);
|
||||
}
|
||||
|
|
@ -53,5 +59,48 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.Configure(setupAction);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void AddDataProtectionServices(IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton<ILoggerFactory>(DataProtectionProviderFactory.GetDefaultLoggerFactory());
|
||||
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
services.AddSingleton<RegistryPolicyResolver>();
|
||||
}
|
||||
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IConfigureOptions<KeyManagementOptions>, KeyManagementOptionsSetup>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IConfigureOptions<DataProtectionOptions>, DataProtectionOptionsSetup>());
|
||||
|
||||
services.AddSingleton<IKeyManager, XmlKeyManager>();
|
||||
|
||||
// Internal services
|
||||
services.AddSingleton<IDefaultKeyResolver, DefaultKeyResolver>();
|
||||
services.AddSingleton<IKeyRingProvider, KeyRingProvider>();
|
||||
|
||||
services.AddSingleton<IDataProtectionProvider>(s =>
|
||||
{
|
||||
var dpOptions = s.GetRequiredService<IOptions<DataProtectionOptions>>();
|
||||
var keyRingProvider = s.GetRequiredService<IKeyRingProvider>();
|
||||
var loggerFactory = s.GetRequiredService<ILoggerFactory>();
|
||||
|
||||
IDataProtectionProvider dataProtectionProvider = null;
|
||||
dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyRingProvider, loggerFactory);
|
||||
|
||||
// Link the provider to the supplied discriminator
|
||||
if (!string.IsNullOrEmpty(dpOptions.Value.ApplicationDiscriminator))
|
||||
{
|
||||
dataProtectionProvider = dataProtectionProvider.CreateProtector(dpOptions.Value.ApplicationDiscriminator);
|
||||
}
|
||||
|
||||
return dataProtectionProvider;
|
||||
});
|
||||
|
||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
services.AddSingleton<ICertificateResolver, CertificateResolver>();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
// 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.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Win32;
|
||||
|
||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Default <see cref="ServiceDescriptor"/> instances for the Data Protection system.
|
||||
/// </summary>
|
||||
internal static class DataProtectionServiceDescriptors
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IConfigureOptions{KeyManagementOptions}"/> where the key lifetime is specified explicitly.
|
||||
/// </summary>
|
||||
|
||||
public static ServiceDescriptor ConfigureOptions_DefaultKeyLifetime(int numDays)
|
||||
{
|
||||
return ServiceDescriptor.Transient<IConfigureOptions<KeyManagementOptions>>(services =>
|
||||
{
|
||||
return new ConfigureOptions<KeyManagementOptions>(options =>
|
||||
{
|
||||
options.NewKeyLifetime = TimeSpan.FromDays(numDays);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IAuthenticatedEncryptorConfiguration"/> backed by an <see cref="IInternalAuthenticatedEncryptionSettings"/>.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IAuthenticatedEncryptorConfiguration_FromSettings(IInternalAuthenticatedEncryptionSettings options)
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IAuthenticatedEncryptorConfiguration>(options.ToConfiguration);
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
/// <summary>
|
||||
/// An <see cref="ICertificateResolver"/> backed by the default implementation.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor ICertificateResolver_Default()
|
||||
{
|
||||
return ServiceDescriptor.Singleton<ICertificateResolver, CertificateResolver>();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// An ephemeral <see cref="IDataProtectionProvider"/>.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IDataProtectionProvider_Ephemeral()
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IDataProtectionProvider>(services => new EphemeralDataProtectionProvider(services));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IKeyEscrowSink"/> backed by a given implementation type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The implementation type name is provided as a string so that we can provide activation services.
|
||||
/// </remarks>
|
||||
public static ServiceDescriptor IKeyEscrowSink_FromTypeName(string implementationTypeName)
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IKeyEscrowSink>(services => services.GetActivator().CreateInstance<IKeyEscrowSink>(implementationTypeName));
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IXmlEncryptor"/> backed by an X.509 certificate.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IXmlEncryptor_Certificate(X509Certificate2 certificate)
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IXmlEncryptor>(services => new CertificateXmlEncryptor(certificate, services));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IXmlEncryptor"/> backed by an X.509 certificate.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IXmlEncryptor_Certificate(string thumbprint)
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IXmlEncryptor>(services => new CertificateXmlEncryptor(
|
||||
thumbprint: thumbprint,
|
||||
certificateResolver: services.GetRequiredService<ICertificateResolver>(),
|
||||
services: services));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IXmlEncryptor"/> backed by DPAPI.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IXmlEncryptor_Dpapi(bool protectToMachine)
|
||||
{
|
||||
CryptoUtil.AssertPlatformIsWindows();
|
||||
return ServiceDescriptor.Singleton<IXmlEncryptor>(services => new DpapiXmlEncryptor(protectToMachine, services));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IXmlEncryptor"/> backed by DPAPI-NG.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IXmlEncryptor_DpapiNG(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags)
|
||||
{
|
||||
CryptoUtil.AssertPlatformIsWindows8OrLater();
|
||||
return ServiceDescriptor.Singleton<IXmlEncryptor>(services => new DpapiNGXmlEncryptor(protectionDescriptorRule, flags, services));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IXmlRepository"/> backed by a file system.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IXmlRepository_FileSystem(DirectoryInfo directory)
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IXmlRepository>(services => new FileSystemXmlRepository(directory, services));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IXmlRepository"/> backed by the Windows registry.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor IXmlRepository_Registry(RegistryKey registryKey)
|
||||
{
|
||||
return ServiceDescriptor.Singleton<IXmlRepository>(services => new RegistryXmlRepository(registryKey, services));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
// 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 Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to default Data Protection <see cref="ServiceDescriptor"/> instances.
|
||||
/// </summary>
|
||||
public static class DataProtectionServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a collection of default <see cref="ServiceDescriptor"/> instances that can be
|
||||
/// used to bootstrap the Data Protection system.
|
||||
/// </summary>
|
||||
public static IEnumerable<ServiceDescriptor> GetDefaultServices()
|
||||
{
|
||||
// The default key services are a strange beast. We don't want to return
|
||||
// IXmlEncryptor and IXmlRepository as-is because they almost always have to be
|
||||
// set as a matched pair. Instead, our built-in key manager will use a meta-service
|
||||
// which represents the default pairing (logic based on hosting environment as
|
||||
// demonstrated below), and if the developer explicitly specifies one or the other
|
||||
// we'll not use the fallback at all.
|
||||
yield return ServiceDescriptor.Singleton<IDefaultKeyServices>(services =>
|
||||
{
|
||||
var log = services.GetLogger(typeof(DataProtectionServices));
|
||||
|
||||
ServiceDescriptor keyEncryptorDescriptor = null;
|
||||
ServiceDescriptor keyRepositoryDescriptor = null;
|
||||
|
||||
// If we're running in Azure Web Sites, the key repository goes in the %HOME% directory.
|
||||
var azureWebSitesKeysFolder = FileSystemXmlRepository.GetKeyStorageDirectoryForAzureWebSites();
|
||||
if (azureWebSitesKeysFolder != null)
|
||||
{
|
||||
log?.UsingAzureAsKeyRepository(azureWebSitesKeysFolder.FullName);
|
||||
|
||||
// Cloud DPAPI isn't yet available, so we don't encrypt keys at rest.
|
||||
// This isn't all that different than what Azure Web Sites does today, and we can always add this later.
|
||||
keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_FileSystem(azureWebSitesKeysFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the user profile is available, store keys in the user profile directory.
|
||||
var localAppDataKeysFolder = FileSystemXmlRepository.DefaultKeyStorageDirectory;
|
||||
if (localAppDataKeysFolder != null)
|
||||
{
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// If the user profile is available, we can protect using DPAPI.
|
||||
// Probe to see if protecting to local user is available, and use it as the default if so.
|
||||
keyEncryptorDescriptor = DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToMachine: !DpapiSecretSerializerHelper.CanProtectToCurrentUserAccount());
|
||||
}
|
||||
keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_FileSystem(localAppDataKeysFolder);
|
||||
|
||||
if (keyEncryptorDescriptor != null)
|
||||
{
|
||||
log?.UsingProfileAsKeyRepositoryWithDPAPI(localAppDataKeysFolder.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
log?.UsingProfileAsKeyRepository(localAppDataKeysFolder.FullName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use profile isn't available - can we use the HKLM registry?
|
||||
RegistryKey regKeyStorageKey = null;
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey;
|
||||
}
|
||||
if (regKeyStorageKey != null)
|
||||
{
|
||||
// If the user profile isn't available, we can protect using DPAPI (to machine).
|
||||
keyEncryptorDescriptor = DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToMachine: true);
|
||||
keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_Registry(regKeyStorageKey);
|
||||
|
||||
log?.UsingRegistryAsKeyRepositoryWithDPAPI(regKeyStorageKey.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Final fallback - use an ephemeral repository since we don't know where else to go.
|
||||
// This can only be used for development scenarios.
|
||||
keyRepositoryDescriptor = ServiceDescriptor.Singleton<IXmlRepository>(
|
||||
s => new EphemeralXmlRepository(s));
|
||||
|
||||
log?.UsingEphemeralKeyRepository();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new DefaultKeyServices(
|
||||
services: services,
|
||||
keyEncryptorDescriptor: keyEncryptorDescriptor,
|
||||
keyRepositoryDescriptor: keyRepositoryDescriptor);
|
||||
});
|
||||
|
||||
// Provide root key management and data protection services
|
||||
yield return ServiceDescriptor.Singleton<IKeyManager>(services => new XmlKeyManager(services));
|
||||
|
||||
yield return ServiceDescriptor.Singleton<IDataProtectionProvider>(
|
||||
services => DataProtectionProviderFactory.GetProviderFromServices(
|
||||
options: services.GetRequiredService<IOptions<DataProtectionOptions>>().Value,
|
||||
services: services,
|
||||
mustCreateImmediately: true /* this is the ultimate fallback */));
|
||||
|
||||
// Provide services required for XML encryption
|
||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
yield return DataProtectionServiceDescriptors.ICertificateResolver_Default();
|
||||
#endif
|
||||
|
||||
// Hook up the logic which allows populating default options
|
||||
yield return ServiceDescriptor.Transient<IConfigureOptions<DataProtectionOptions>>(services =>
|
||||
{
|
||||
return new ConfigureOptions<DataProtectionOptions>(options =>
|
||||
{
|
||||
options.ApplicationDiscriminator = services.GetApplicationUniqueIdentifier();
|
||||
});
|
||||
});
|
||||
|
||||
// Read and apply policy from the registry, overriding any other defaults.
|
||||
var encryptorConfigurationReadFromRegistry = false;
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
foreach (var descriptor in RegistryPolicyResolver.ResolveDefaultPolicy())
|
||||
{
|
||||
yield return descriptor;
|
||||
if (descriptor.ServiceType == typeof(IAuthenticatedEncryptorConfiguration))
|
||||
{
|
||||
encryptorConfigurationReadFromRegistry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, provide a fallback encryptor configuration if one wasn't already specified.
|
||||
if (!encryptorConfigurationReadFromRegistry)
|
||||
{
|
||||
yield return DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(
|
||||
new AuthenticatedEncryptionSettings());;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -22,36 +23,28 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
{
|
||||
private readonly KeyRingBasedDataProtectionProvider _dataProtectionProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an ephemeral <see cref="IDataProtectionProvider"/>.
|
||||
/// </summary>
|
||||
public EphemeralDataProtectionProvider()
|
||||
: this(services: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an ephemeral <see cref="IDataProtectionProvider"/>, optionally providing
|
||||
/// services (such as logging) for consumption by the provider.
|
||||
/// </summary>
|
||||
public EphemeralDataProtectionProvider(IServiceProvider services)
|
||||
public EphemeralDataProtectionProvider(ILoggerFactory loggerFactory)
|
||||
{
|
||||
IKeyRingProvider keyringProvider;
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// Fastest implementation: AES-256-GCM [CNG]
|
||||
keyringProvider = new EphemeralKeyRing<CngGcmAuthenticatedEncryptionSettings>();
|
||||
keyringProvider = new EphemeralKeyRing<CngGcmAuthenticatedEncryptorConfiguration>(loggerFactory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Slowest implementation: AES-256-CBC + HMACSHA256 [Managed]
|
||||
keyringProvider = new EphemeralKeyRing<ManagedAuthenticatedEncryptionSettings>();
|
||||
keyringProvider = new EphemeralKeyRing<ManagedAuthenticatedEncryptorConfiguration>(loggerFactory);
|
||||
}
|
||||
|
||||
var logger = services.GetLogger<EphemeralDataProtectionProvider>();
|
||||
logger?.UsingEphemeralDataProtectionProvider();
|
||||
var logger = loggerFactory.CreateLogger<EphemeralDataProtectionProvider>();
|
||||
logger.UsingEphemeralDataProtectionProvider();
|
||||
|
||||
_dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyringProvider, services);
|
||||
_dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyringProvider, loggerFactory);
|
||||
}
|
||||
|
||||
public IDataProtector CreateProtector(string purpose)
|
||||
|
|
@ -66,12 +59,17 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
}
|
||||
|
||||
private sealed class EphemeralKeyRing<T> : IKeyRing, IKeyRingProvider
|
||||
where T : IInternalAuthenticatedEncryptionSettings, new()
|
||||
where T : AlgorithmConfiguration, new()
|
||||
{
|
||||
public EphemeralKeyRing(ILoggerFactory loggerFactory)
|
||||
{
|
||||
DefaultAuthenticatedEncryptor = GetDefaultEncryptor(loggerFactory);
|
||||
}
|
||||
|
||||
// Currently hardcoded to a 512-bit KDK.
|
||||
private const int NUM_BYTES_IN_KDK = 512 / 8;
|
||||
|
||||
public IAuthenticatedEncryptor DefaultAuthenticatedEncryptor { get; } = new T().ToConfiguration(services: null).CreateNewDescriptor().CreateEncryptorInstance();
|
||||
public IAuthenticatedEncryptor DefaultAuthenticatedEncryptor { get; }
|
||||
|
||||
public Guid DefaultKeyId { get; } = default(Guid);
|
||||
|
||||
|
|
@ -85,6 +83,29 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
private static IAuthenticatedEncryptor GetDefaultEncryptor(ILoggerFactory loggerFactory)
|
||||
{
|
||||
var configuration = new T();
|
||||
if (configuration is CngGcmAuthenticatedEncryptorConfiguration)
|
||||
{
|
||||
var descriptor = (CngGcmAuthenticatedEncryptorDescriptor)new T().CreateNewDescriptor();
|
||||
return new CngGcmAuthenticatedEncryptorFactory(loggerFactory)
|
||||
.CreateAuthenticatedEncryptorInstance(
|
||||
descriptor.MasterKey,
|
||||
configuration as CngGcmAuthenticatedEncryptorConfiguration);
|
||||
}
|
||||
else if (configuration is ManagedAuthenticatedEncryptorConfiguration)
|
||||
{
|
||||
var descriptor = (ManagedAuthenticatedEncryptorDescriptor)new T().CreateNewDescriptor();
|
||||
return new ManagedAuthenticatedEncryptorFactory(loggerFactory)
|
||||
.CreateAuthenticatedEncryptorInstance(
|
||||
descriptor.MasterKey,
|
||||
configuration as ManagedAuthenticatedEncryptorConfiguration);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
|
||||
public static InvalidOperationException Common_PropertyCannotBeNullOrEmpty(string propertyName)
|
||||
{
|
||||
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyCannotBeNullOrEmpty, propertyName);
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyCannotBeNullOrEmpty, propertyName);
|
||||
return new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
public static InvalidOperationException Common_PropertyMustBeNonNegative(string propertyName)
|
||||
{
|
||||
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyMustBeNonNegative, propertyName);
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyMustBeNonNegative, propertyName);
|
||||
return new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
|
|
@ -56,13 +56,13 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
|
||||
public static CryptographicException Common_KeyNotFound(Guid id)
|
||||
{
|
||||
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyNotFound, id);
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_KeyNotFound, id);
|
||||
return new CryptographicException(message);
|
||||
}
|
||||
|
||||
public static CryptographicException Common_KeyRevoked(Guid id)
|
||||
{
|
||||
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyRevoked, id);
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_KeyRevoked, id);
|
||||
return new CryptographicException(message);
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
|
||||
public static InvalidOperationException XmlKeyManager_DuplicateKey(Guid keyId)
|
||||
{
|
||||
var message = String.Format(CultureInfo.CurrentCulture, Resources.XmlKeyManager_DuplicateKey, keyId);
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resources.XmlKeyManager_DuplicateKey, keyId);
|
||||
return new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +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 Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection
|
||||
{
|
||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// <para>
|
||||
/// Similarly, when a developer modifies the default protected payload cryptographic
|
||||
/// algorithms, it is intended that he also select an explitiy key storage location.
|
||||
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptionSettings)"/>
|
||||
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptorConfiguration)"/>
|
||||
/// should therefore generally be paired with a call to <see cref="DataProtectionBuilderExtensions.PersistKeysToFileSystem(IDataProtectionBuilder,DirectoryInfo)"/>,
|
||||
/// for example.
|
||||
/// </para>
|
||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// <para>
|
||||
/// Similarly, when a developer modifies the default protected payload cryptographic
|
||||
/// algorithms, it is intended that he also select an explitiy key storage location.
|
||||
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptionSettings)"/>
|
||||
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptorConfiguration)"/>
|
||||
/// should therefore generally be paired with a call to <see cref="DataProtectionBuilderExtensions.PersistKeysToFileSystem(IDataProtectionBuilder,DirectoryInfo)"/>,
|
||||
/// for example.
|
||||
/// </para>
|
||||
|
|
|
|||
|
|
@ -2,18 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Win32;
|
||||
|
||||
#if !DOTNET5_4 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.Internal
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.Internal
|
||||
{
|
||||
internal class DataProtectionOptionsSetup : IConfigureOptions<DataProtectionOptions>
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public DataProtectionOptionsSetup(IServiceProvider provider)
|
||||
{
|
||||
_services = provider;
|
||||
}
|
||||
|
||||
public void Configure(DataProtectionOptions options)
|
||||
{
|
||||
options.ApplicationDiscriminator = _services.GetApplicationUniqueIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.Internal
|
||||
{
|
||||
internal class KeyManagementOptionsSetup : IConfigureOptions<KeyManagementOptions>
|
||||
{
|
||||
private readonly RegistryPolicyResolver _registryPolicyResolver;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public KeyManagementOptionsSetup(ILoggerFactory loggerFactory) : this(loggerFactory, registryPolicyResolver: null)
|
||||
{
|
||||
}
|
||||
|
||||
public KeyManagementOptionsSetup(ILoggerFactory loggerFactory, RegistryPolicyResolver registryPolicyResolver)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
_registryPolicyResolver = registryPolicyResolver;
|
||||
}
|
||||
|
||||
public void Configure(KeyManagementOptions options)
|
||||
{
|
||||
RegistryPolicy context = null;
|
||||
if (_registryPolicyResolver != null)
|
||||
{
|
||||
context = _registryPolicyResolver.ResolvePolicy();
|
||||
}
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
if (context.DefaultKeyLifetime.HasValue)
|
||||
{
|
||||
options.NewKeyLifetime = TimeSpan.FromDays(context.DefaultKeyLifetime.Value);
|
||||
}
|
||||
|
||||
options.AuthenticatedEncryptorConfiguration = context.EncryptorConfiguration;
|
||||
|
||||
var escrowSinks = context.KeyEscrowSinks;
|
||||
if (escrowSinks != null)
|
||||
{
|
||||
foreach (var escrowSink in escrowSinks)
|
||||
{
|
||||
options.KeyEscrowSinks.Add(escrowSink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.AuthenticatedEncryptorConfiguration == null)
|
||||
{
|
||||
options.AuthenticatedEncryptorConfiguration = new AuthenticatedEncryptorConfiguration();
|
||||
}
|
||||
|
||||
options.AuthenticatedEncryptorFactories.Add(new CngGcmAuthenticatedEncryptorFactory(_loggerFactory));
|
||||
options.AuthenticatedEncryptorFactories.Add(new CngCbcAuthenticatedEncryptorFactory(_loggerFactory));
|
||||
options.AuthenticatedEncryptorFactories.Add(new ManagedAuthenticatedEncryptorFactory(_loggerFactory));
|
||||
options.AuthenticatedEncryptorFactories.Add(new AuthenticatedEncryptorFactory(_loggerFactory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Cryptography;
|
|||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||
{
|
||||
|
|
@ -28,6 +29,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IEnumerable<IAuthenticatedEncryptorFactory> _encryptorFactories;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum skew that is allowed between servers.
|
||||
/// This is used to allow newly-created keys to be used across servers even though
|
||||
|
|
@ -39,23 +42,38 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
/// </remarks>
|
||||
private readonly TimeSpan _maxServerToServerClockSkew;
|
||||
|
||||
public DefaultKeyResolver(TimeSpan keyPropagationWindow, TimeSpan maxServerToServerClockSkew, IServiceProvider services)
|
||||
public DefaultKeyResolver(IOptions<KeyManagementOptions> keyManagementOptions, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_keyPropagationWindow = keyPropagationWindow;
|
||||
_maxServerToServerClockSkew = maxServerToServerClockSkew;
|
||||
_logger = services.GetLogger<DefaultKeyResolver>();
|
||||
_keyPropagationWindow = keyManagementOptions.Value.KeyPropagationWindow;
|
||||
_maxServerToServerClockSkew = keyManagementOptions.Value.MaxServerClockSkew;
|
||||
_encryptorFactories = keyManagementOptions.Value.AuthenticatedEncryptorFactories;
|
||||
_logger = loggerFactory.CreateLogger<DefaultKeyResolver>();
|
||||
}
|
||||
|
||||
private bool CanCreateAuthenticatedEncryptor(IKey key)
|
||||
{
|
||||
try
|
||||
{
|
||||
var encryptorInstance = key.CreateEncryptorInstance() ?? CryptoUtil.Fail<IAuthenticatedEncryptor>("CreateEncryptorInstance returned null.");
|
||||
IAuthenticatedEncryptor encryptorInstance = null;
|
||||
foreach (var factory in _encryptorFactories)
|
||||
{
|
||||
encryptorInstance = factory.CreateEncryptorInstance(key);
|
||||
if (encryptorInstance != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (encryptorInstance == null)
|
||||
{
|
||||
CryptoUtil.Fail<IAuthenticatedEncryptor>("CreateEncryptorInstance returned null.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.KeyIsIneligibleToBeTheDefaultKeyBecauseItsMethodFailed(key.KeyId, nameof(IKey.CreateEncryptorInstance), ex);
|
||||
_logger.KeyIsIneligibleToBeTheDefaultKeyBecauseItsMethodFailed(key.KeyId, nameof(IAuthenticatedEncryptorFactory.CreateEncryptorInstance), ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -70,12 +88,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
|
||||
if (preferredDefaultKey != null)
|
||||
{
|
||||
_logger?.ConsideringKeyWithExpirationDateAsDefaultKey(preferredDefaultKey.KeyId, preferredDefaultKey.ExpirationDate);
|
||||
_logger.ConsideringKeyWithExpirationDateAsDefaultKey(preferredDefaultKey.KeyId, preferredDefaultKey.ExpirationDate);
|
||||
|
||||
// if the key has been revoked or is expired, it is no longer a candidate
|
||||
if (preferredDefaultKey.IsRevoked || preferredDefaultKey.IsExpired(now) || !CanCreateAuthenticatedEncryptor(preferredDefaultKey))
|
||||
{
|
||||
_logger?.KeyIsNoLongerUnderConsiderationAsDefault(preferredDefaultKey.KeyId);
|
||||
_logger.KeyIsNoLongerUnderConsiderationAsDefault(preferredDefaultKey.KeyId);
|
||||
preferredDefaultKey = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -98,7 +116,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
|
||||
if (callerShouldGenerateNewKey)
|
||||
{
|
||||
_logger?.DefaultKeyExpirationImminentAndRepository();
|
||||
_logger.DefaultKeyExpirationImminentAndRepository();
|
||||
}
|
||||
|
||||
fallbackKey = null;
|
||||
|
|
@ -119,7 +137,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
where !key.IsRevoked && CanCreateAuthenticatedEncryptor(key)
|
||||
select key).FirstOrDefault();
|
||||
|
||||
_logger?.RepositoryContainsNoViableDefaultKey();
|
||||
_logger.RepositoryContainsNoViableDefaultKey();
|
||||
|
||||
callerShouldGenerateNewKey = true;
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
|
|
@ -23,11 +22,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
DateTimeOffset expirationDate,
|
||||
IInternalXmlKeyManager keyManager,
|
||||
XElement keyElement)
|
||||
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptor>(GetLazyEncryptorDelegate(keyManager, keyElement)))
|
||||
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptorDescriptor>(GetLazyDescriptorDelegate(keyManager, keyElement)))
|
||||
{
|
||||
}
|
||||
|
||||
private static Func<IAuthenticatedEncryptor> GetLazyEncryptorDelegate(IInternalXmlKeyManager keyManager, XElement keyElement)
|
||||
private static Func<IAuthenticatedEncryptorDescriptor> GetLazyDescriptorDelegate(IInternalXmlKeyManager keyManager, XElement keyElement)
|
||||
{
|
||||
// The <key> element will be held around in memory for a potentially lengthy period
|
||||
// of time. Since it might contain sensitive information, we should protect it.
|
||||
|
|
@ -35,7 +34,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
|
||||
try
|
||||
{
|
||||
return () => keyManager.DeserializeDescriptorFromKeyElement(encryptedKeyElement.ToXElement()).CreateEncryptorInstance();
|
||||
return () => keyManager.DeserializeDescriptorFromKeyElement(encryptedKeyElement.ToXElement());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||
{
|
||||
|
|
@ -45,10 +46,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
Guid KeyId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an IAuthenticatedEncryptor instance that can be used to encrypt data
|
||||
/// to and decrypt data from this key.
|
||||
/// Gets the <see cref="IAuthenticatedEncryptorDescriptor"/> instance associated with this key.
|
||||
/// </summary>
|
||||
/// <returns>An IAuthenticatedEncryptor.</returns>
|
||||
IAuthenticatedEncryptor CreateEncryptorInstance();
|
||||
IAuthenticatedEncryptorDescriptor Descriptor { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
|
||||
{
|
||||
|
|
@ -14,8 +15,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
|
|||
{
|
||||
private readonly CancellationToken _expirationToken;
|
||||
|
||||
internal CacheableKeyRing(CancellationToken expirationToken, DateTimeOffset expirationTime, IKey defaultKey, IEnumerable<IKey> allKeys)
|
||||
: this(expirationToken, expirationTime, keyRing: new KeyRing(defaultKey, allKeys))
|
||||
internal CacheableKeyRing(CancellationToken expirationToken, DateTimeOffset expirationTime, IKey defaultKey, IEnumerable<IKey> allKeys, IEnumerable<IAuthenticatedEncryptorFactory> encryptorFactories)
|
||||
: this(expirationToken, expirationTime, keyRing: new KeyRing(defaultKey, allKeys, encryptorFactories))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +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;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
|
||||
{
|
||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
|
|||
/// The default key, may be null if no key is a good default candidate.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this property is non-null, its <see cref="IKey.CreateEncryptorInstance"/> method will succeed
|
||||
/// If this property is non-null, its <see cref="IAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)"/> method will succeed
|
||||
/// so is appropriate for use with deferred keys.
|
||||
/// </remarks>
|
||||
public IKey DefaultKey;
|
||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
|
|||
/// be null if there is no viable fallback key.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this property is non-null, its <see cref="IKey.CreateEncryptorInstance"/> method will succeed
|
||||
/// If this property is non-null, its <see cref="IAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)"/> method will succeed
|
||||
/// so is appropriate for use with deferred keys.
|
||||
/// </remarks>
|
||||
public IKey FallbackKey;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
internal sealed class Key : KeyBase
|
||||
{
|
||||
public Key(Guid keyId, DateTimeOffset creationDate, DateTimeOffset activationDate, DateTimeOffset expirationDate, IAuthenticatedEncryptorDescriptor descriptor)
|
||||
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptor>(descriptor.CreateEncryptorInstance))
|
||||
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptorDescriptor>(() => descriptor))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||
{
|
||||
|
|
@ -11,15 +12,15 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
/// </summary>
|
||||
internal abstract class KeyBase : IKey
|
||||
{
|
||||
private readonly Lazy<IAuthenticatedEncryptor> _lazyEncryptor;
|
||||
private readonly Lazy<IAuthenticatedEncryptorDescriptor> _lazyDescriptor;
|
||||
|
||||
public KeyBase(Guid keyId, DateTimeOffset creationDate, DateTimeOffset activationDate, DateTimeOffset expirationDate, Lazy<IAuthenticatedEncryptor> lazyEncryptor)
|
||||
public KeyBase(Guid keyId, DateTimeOffset creationDate, DateTimeOffset activationDate, DateTimeOffset expirationDate, Lazy<IAuthenticatedEncryptorDescriptor> lazyDescriptor)
|
||||
{
|
||||
KeyId = keyId;
|
||||
CreationDate = creationDate;
|
||||
ActivationDate = activationDate;
|
||||
ExpirationDate = expirationDate;
|
||||
_lazyEncryptor = lazyEncryptor;
|
||||
_lazyDescriptor = lazyDescriptor;
|
||||
}
|
||||
|
||||
public DateTimeOffset ActivationDate { get; }
|
||||
|
|
@ -32,9 +33,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
|
||||
public Guid KeyId { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
public IAuthenticatedEncryptorDescriptor Descriptor
|
||||
{
|
||||
return _lazyEncryptor.Value;
|
||||
get
|
||||
{
|
||||
return _lazyDescriptor.Value;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetRevoked()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
// 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 Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||
{
|
||||
|
|
@ -24,8 +29,21 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
if (other != null)
|
||||
{
|
||||
this.AutoGenerateKeys = other.AutoGenerateKeys;
|
||||
this._newKeyLifetime = other._newKeyLifetime;
|
||||
AutoGenerateKeys = other.AutoGenerateKeys;
|
||||
_newKeyLifetime = other._newKeyLifetime;
|
||||
XmlEncryptor = other.XmlEncryptor;
|
||||
XmlRepository = other.XmlRepository;
|
||||
AuthenticatedEncryptorConfiguration = other.AuthenticatedEncryptorConfiguration;
|
||||
|
||||
foreach (var keyEscrowSink in other.KeyEscrowSinks)
|
||||
{
|
||||
KeyEscrowSinks.Add(keyEscrowSink);
|
||||
}
|
||||
|
||||
foreach (var encryptorFactory in other.AuthenticatedEncryptorFactories)
|
||||
{
|
||||
AuthenticatedEncryptorFactories.Add(encryptorFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,5 +137,32 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
_newKeyLifetime = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="AlgorithmConfiguration"/> instance that can be used to create
|
||||
/// the <see cref="IAuthenticatedEncryptorDescriptor"/> instance.
|
||||
/// </summary>
|
||||
public AlgorithmConfiguration AuthenticatedEncryptorConfiguration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of <see cref="IKeyEscrowSink"/> to store the key material in.
|
||||
/// </summary>
|
||||
public IList<IKeyEscrowSink> KeyEscrowSinks { get; } = new List<IKeyEscrowSink>();
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IXmlRepository"/> to use for storing and retrieving XML elements.
|
||||
/// </summary>
|
||||
public IXmlRepository XmlRepository { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IXmlEncryptor"/> to use for encrypting XML elements.
|
||||
/// </summary>
|
||||
public IXmlEncryptor XmlEncryptor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of <see cref="IAuthenticatedEncryptorFactory"/> that will be used for creating
|
||||
/// <see cref="IAuthenticatedEncryptor"/>s.
|
||||
/// </summary>
|
||||
public IList<IAuthenticatedEncryptorFactory> AuthenticatedEncryptorFactories { get; } = new List<IAuthenticatedEncryptorFactory>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
private readonly KeyHolder _defaultKeyHolder;
|
||||
private readonly Dictionary<Guid, KeyHolder> _keyIdToKeyHolderMap;
|
||||
|
||||
public KeyRing(IKey defaultKey, IEnumerable<IKey> allKeys)
|
||||
public KeyRing(IKey defaultKey, IEnumerable<IKey> allKeys, IEnumerable<IAuthenticatedEncryptorFactory> encryptorFactories)
|
||||
{
|
||||
_keyIdToKeyHolderMap = new Dictionary<Guid, KeyHolder>();
|
||||
foreach (IKey key in allKeys)
|
||||
{
|
||||
_keyIdToKeyHolderMap.Add(key.KeyId, new KeyHolder(key));
|
||||
_keyIdToKeyHolderMap.Add(key.KeyId, new KeyHolder(key, encryptorFactories));
|
||||
}
|
||||
|
||||
// It's possible under some circumstances that the default key won't be part of 'allKeys',
|
||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
// wasn't in the underlying repository. In this case, we just add it now.
|
||||
if (!_keyIdToKeyHolderMap.ContainsKey(defaultKey.KeyId))
|
||||
{
|
||||
_keyIdToKeyHolderMap.Add(defaultKey.KeyId, new KeyHolder(defaultKey));
|
||||
_keyIdToKeyHolderMap.Add(defaultKey.KeyId, new KeyHolder(defaultKey, encryptorFactories));
|
||||
}
|
||||
|
||||
DefaultKeyId = defaultKey.KeyId;
|
||||
|
|
@ -61,17 +61,19 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
private readonly IKey _key;
|
||||
private IAuthenticatedEncryptor _encryptor;
|
||||
private readonly IEnumerable<IAuthenticatedEncryptorFactory> _encryptorFactories;
|
||||
|
||||
internal KeyHolder(IKey key)
|
||||
internal KeyHolder(IKey key, IEnumerable<IAuthenticatedEncryptorFactory> encryptorFactories)
|
||||
{
|
||||
_key = key;
|
||||
_encryptorFactories = encryptorFactories;
|
||||
}
|
||||
|
||||
internal IAuthenticatedEncryptor GetEncryptorInstance(out bool isRevoked)
|
||||
{
|
||||
// simple double-check lock pattern
|
||||
// we can't use LazyInitializer<T> because we don't have a simple value factory
|
||||
var encryptor = Volatile.Read(ref _encryptor);
|
||||
IAuthenticatedEncryptor encryptor = Volatile.Read(ref _encryptor);
|
||||
if (encryptor == null)
|
||||
{
|
||||
lock (this)
|
||||
|
|
@ -79,7 +81,14 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
encryptor = Volatile.Read(ref _encryptor);
|
||||
if (encryptor == null)
|
||||
{
|
||||
encryptor = _key.CreateEncryptorInstance();
|
||||
foreach (var factory in _encryptorFactories)
|
||||
{
|
||||
encryptor = factory.CreateEncryptorInstance(_key);
|
||||
if (encryptor != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
Volatile.Write(ref _encryptor, encryptor);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
private readonly IKeyRingProvider _keyRingProvider;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public KeyRingBasedDataProtectionProvider(IKeyRingProvider keyRingProvider, IServiceProvider services)
|
||||
public KeyRingBasedDataProtectionProvider(IKeyRingProvider keyRingProvider, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_keyRingProvider = keyRingProvider;
|
||||
_logger = services.GetLogger<KeyRingBasedDataProtector>(); // note: for protector (not provider!) type, could be null
|
||||
_logger = loggerFactory.CreateLogger<KeyRingBasedDataProtector>(); // note: for protector (not provider!) type
|
||||
}
|
||||
|
||||
public IDataProtector CreateProtector(string purpose)
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
var requestedEncryptor = currentKeyRing.GetAuthenticatedEncryptorByKeyId(keyIdFromPayload, out keyWasRevoked);
|
||||
if (requestedEncryptor == null)
|
||||
{
|
||||
_logger?.KeyWasNotFoundInTheKeyRingUnprotectOperationCannotProceed(keyIdFromPayload);
|
||||
_logger.KeyWasNotFoundInTheKeyRingUnprotectOperationCannotProceed(keyIdFromPayload);
|
||||
throw Error.Common_KeyNotFound(keyIdFromPayload);
|
||||
}
|
||||
|
||||
|
|
@ -262,12 +262,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
if (allowOperationsOnRevokedKeys)
|
||||
{
|
||||
_logger?.KeyWasRevokedCallerRequestedUnprotectOperationProceedRegardless(keyIdFromPayload);
|
||||
_logger.KeyWasRevokedCallerRequestedUnprotectOperationProceedRegardless(keyIdFromPayload);
|
||||
status = UnprotectStatus.DecryptionKeyWasRevoked;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.KeyWasRevokedUnprotectOperationCannotProceed(keyIdFromPayload);
|
||||
_logger.KeyWasRevokedUnprotectOperationCannotProceed(keyIdFromPayload);
|
||||
throw Error.Common_KeyRevoked(keyIdFromPayload);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||
{
|
||||
|
|
@ -22,14 +23,32 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
private readonly IKeyManager _keyManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public KeyRingProvider(IKeyManager keyManager, KeyManagementOptions keyManagementOptions, IServiceProvider services)
|
||||
public KeyRingProvider(
|
||||
IKeyManager keyManager,
|
||||
IOptions<KeyManagementOptions> keyManagementOptions,
|
||||
IDefaultKeyResolver defaultKeyResolver,
|
||||
ILoggerFactory loggerFactory)
|
||||
: this(
|
||||
keyManager,
|
||||
keyManagementOptions,
|
||||
cacheableKeyRingProvider: null,
|
||||
defaultKeyResolver: defaultKeyResolver,
|
||||
loggerFactory: loggerFactory)
|
||||
{
|
||||
_keyManagementOptions = new KeyManagementOptions(keyManagementOptions); // clone so new instance is immutable
|
||||
}
|
||||
|
||||
public KeyRingProvider(
|
||||
IKeyManager keyManager,
|
||||
IOptions<KeyManagementOptions> keyManagementOptions,
|
||||
ICacheableKeyRingProvider cacheableKeyRingProvider,
|
||||
IDefaultKeyResolver defaultKeyResolver,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_keyManagementOptions = new KeyManagementOptions(keyManagementOptions.Value); // clone so new instance is immutable
|
||||
_keyManager = keyManager;
|
||||
_cacheableKeyRingProvider = services?.GetService<ICacheableKeyRingProvider>() ?? this;
|
||||
_logger = services?.GetLogger<KeyRingProvider>();
|
||||
_defaultKeyResolver = services?.GetService<IDefaultKeyResolver>()
|
||||
?? new DefaultKeyResolver(_keyManagementOptions.KeyPropagationWindow, _keyManagementOptions.MaxServerClockSkew, services);
|
||||
_cacheableKeyRingProvider = cacheableKeyRingProvider ?? this;
|
||||
_defaultKeyResolver = defaultKeyResolver;
|
||||
_logger = loggerFactory.CreateLogger<KeyRingProvider>();
|
||||
}
|
||||
|
||||
private CacheableKeyRing CreateCacheableKeyRingCore(DateTimeOffset now, IKey keyJustAdded)
|
||||
|
|
@ -46,7 +65,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, defaultKeyPolicy.DefaultKey, allKeys);
|
||||
}
|
||||
|
||||
_logger?.PolicyResolutionStatesThatANewKeyShouldBeAddedToTheKeyRing();
|
||||
_logger.PolicyResolutionStatesThatANewKeyShouldBeAddedToTheKeyRing();
|
||||
|
||||
// We shouldn't call CreateKey more than once, else we risk stack diving. This code path shouldn't
|
||||
// get hit unless there was an ineligible key with an activation date slightly later than the one we
|
||||
|
|
@ -67,12 +86,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
var keyToUse = defaultKeyPolicy.DefaultKey ?? defaultKeyPolicy.FallbackKey;
|
||||
if (keyToUse == null)
|
||||
{
|
||||
_logger?.KeyRingDoesNotContainValidDefaultKey();
|
||||
_logger.KeyRingDoesNotContainValidDefaultKey();
|
||||
throw new InvalidOperationException(Resources.KeyRingProvider_NoDefaultKey_AutoGenerateDisabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.UsingFallbackKeyWithExpirationAsDefaultKey(keyToUse.KeyId, keyToUse.ExpirationDate);
|
||||
_logger.UsingFallbackKeyWithExpirationAsDefaultKey(keyToUse.KeyId, keyToUse.ExpirationDate);
|
||||
return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, keyToUse, allKeys);
|
||||
}
|
||||
}
|
||||
|
|
@ -99,9 +118,9 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
Debug.Assert(defaultKey != null);
|
||||
|
||||
// Invariant: our caller ensures that CreateEncryptorInstance succeeded at least once
|
||||
Debug.Assert(defaultKey.CreateEncryptorInstance() != null);
|
||||
Debug.Assert(CreateEncryptorForKey(defaultKey) != null);
|
||||
|
||||
_logger?.UsingKeyAsDefaultKey(defaultKey.KeyId);
|
||||
_logger.UsingKeyAsDefaultKey(defaultKey.KeyId);
|
||||
|
||||
var nextAutoRefreshTime = now + GetRefreshPeriodWithJitter(_keyManagementOptions.KeyRingRefreshPeriod);
|
||||
|
||||
|
|
@ -116,7 +135,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
expirationToken: cacheExpirationToken,
|
||||
expirationTime: (defaultKey.ExpirationDate <= now) ? nextAutoRefreshTime : Min(defaultKey.ExpirationDate, nextAutoRefreshTime),
|
||||
defaultKey: defaultKey,
|
||||
allKeys: allKeys);
|
||||
allKeys: allKeys,
|
||||
encryptorFactories: _keyManagementOptions.AuthenticatedEncryptorFactories);
|
||||
}
|
||||
|
||||
public IKeyRing GetCurrentKeyRing()
|
||||
|
|
@ -156,7 +176,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
|
||||
if (existingCacheableKeyRing != null)
|
||||
{
|
||||
_logger?.ExistingCachedKeyRingIsExpired();
|
||||
_logger.ExistingCachedKeyRingIsExpired();
|
||||
}
|
||||
|
||||
// It's up to us to refresh the cached keyring.
|
||||
|
|
@ -171,11 +191,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
if (existingCacheableKeyRing != null)
|
||||
{
|
||||
_logger?.ErrorOccurredWhileRefreshingKeyRing(ex);
|
||||
_logger.ErrorOccurredWhileRefreshingKeyRing(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.ErrorOccurredWhileReadingKeyRing(ex);
|
||||
_logger.ErrorOccurredWhileReadingKeyRing(ex);
|
||||
}
|
||||
|
||||
// Failures that occur while refreshing the keyring are most likely transient, perhaps due to a
|
||||
|
|
@ -216,6 +236,20 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
}
|
||||
}
|
||||
|
||||
private IAuthenticatedEncryptor CreateEncryptorForKey(IKey key)
|
||||
{
|
||||
foreach (var factory in _keyManagementOptions.AuthenticatedEncryptorFactories)
|
||||
{
|
||||
var encryptor = factory.CreateEncryptorInstance(key);
|
||||
if (encryptor != null)
|
||||
{
|
||||
return encryptor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TimeSpan GetRefreshPeriodWithJitter(TimeSpan refreshPeriod)
|
||||
{
|
||||
// We'll fudge the refresh period up to -20% so that multiple applications don't try to
|
||||
|
|
|
|||
|
|
@ -4,21 +4,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using static System.FormattableString;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||
{
|
||||
|
|
@ -43,9 +45,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
private const string RevokeAllKeysValue = "*";
|
||||
|
||||
private readonly IActivator _activator;
|
||||
private readonly IAuthenticatedEncryptorConfiguration _authenticatedEncryptorConfiguration;
|
||||
private readonly IInternalXmlKeyManager _internalKeyManager;
|
||||
private readonly AlgorithmConfiguration _authenticatedEncryptorConfiguration;
|
||||
private readonly IKeyEscrowSink _keyEscrowSink;
|
||||
private readonly IInternalXmlKeyManager _internalKeyManager;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private CancellationTokenSource _cacheExpirationTokenSource;
|
||||
|
|
@ -53,59 +56,49 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
/// <summary>
|
||||
/// Creates an <see cref="XmlKeyManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="repository">The repository where keys are stored.</param>
|
||||
/// <param name="configuration">Configuration for newly-created keys.</param>
|
||||
/// <param name="services">A provider of optional services.</param>
|
||||
public XmlKeyManager(
|
||||
IXmlRepository repository,
|
||||
IAuthenticatedEncryptorConfiguration configuration,
|
||||
IServiceProvider services)
|
||||
/// <param name="keyManagementOptions">The <see cref="IOptions{KeyManagementOptions}"/> instance that provides the configuration.</param>
|
||||
/// <param name="activator">The <see cref="IActivator"/>.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
public XmlKeyManager(IOptions<KeyManagementOptions> keyManagementOptions, IActivator activator, ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (repository == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(repository));
|
||||
}
|
||||
_loggerFactory = loggerFactory;
|
||||
_logger = _loggerFactory.CreateLogger<XmlKeyManager>();
|
||||
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
KeyEncryptor = services.GetService<IXmlEncryptor>(); // optional
|
||||
KeyRepository = repository;
|
||||
|
||||
_activator = services.GetActivator(); // returns non-null
|
||||
_authenticatedEncryptorConfiguration = configuration;
|
||||
_internalKeyManager = services.GetService<IInternalXmlKeyManager>() ?? this;
|
||||
_keyEscrowSink = services.GetKeyEscrowSink(); // not required
|
||||
_logger = services.GetLogger<XmlKeyManager>(); // not required
|
||||
TriggerAndResetCacheExpirationToken(suppressLogging: true);
|
||||
}
|
||||
|
||||
internal XmlKeyManager(IServiceProvider services)
|
||||
{
|
||||
// First, see if an explicit encryptor or repository was specified.
|
||||
// If either was specified, then we won't use the fallback.
|
||||
KeyEncryptor = services.GetService<IXmlEncryptor>(); // optional
|
||||
KeyRepository = (KeyEncryptor != null)
|
||||
? services.GetRequiredService<IXmlRepository>() // required if encryptor is specified
|
||||
: services.GetService<IXmlRepository>(); // optional if encryptor not specified
|
||||
|
||||
// If the repository is missing, then we get both the encryptor and the repository from the fallback.
|
||||
// If the fallback is missing, the final call to GetRequiredService below will throw.
|
||||
KeyRepository = keyManagementOptions.Value.XmlRepository;
|
||||
KeyEncryptor = keyManagementOptions.Value.XmlEncryptor;
|
||||
if (KeyRepository == null)
|
||||
{
|
||||
var defaultKeyServices = services.GetService<IDefaultKeyServices>();
|
||||
KeyEncryptor = defaultKeyServices?.GetKeyEncryptor(); // optional
|
||||
KeyRepository = defaultKeyServices?.GetKeyRepository() ?? services.GetRequiredService<IXmlRepository>();
|
||||
if (KeyEncryptor != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatXmlKeyManager_IXmlRepositoryNotFound(nameof(IXmlRepository), nameof(IXmlEncryptor)));
|
||||
}
|
||||
else
|
||||
{
|
||||
var keyRepositoryEncryptorPair = GetFallbackKeyRepositoryEncryptorPair();
|
||||
KeyRepository = keyRepositoryEncryptorPair.Key;
|
||||
KeyEncryptor = keyRepositoryEncryptorPair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
_activator = services.GetActivator(); // returns non-null
|
||||
_authenticatedEncryptorConfiguration = services.GetRequiredService<IAuthenticatedEncryptorConfiguration>();
|
||||
_internalKeyManager = services.GetService<IInternalXmlKeyManager>() ?? this;
|
||||
_keyEscrowSink = services.GetKeyEscrowSink(); // not required
|
||||
_logger = services.GetLogger<XmlKeyManager>(); // not required
|
||||
_authenticatedEncryptorConfiguration = keyManagementOptions.Value.AuthenticatedEncryptorConfiguration;
|
||||
|
||||
var escrowSinks = keyManagementOptions.Value.KeyEscrowSinks;
|
||||
_keyEscrowSink = escrowSinks.Count > 0 ? new AggregateKeyEscrowSink(escrowSinks) : null;
|
||||
_activator = activator;
|
||||
TriggerAndResetCacheExpirationToken(suppressLogging: true);
|
||||
_internalKeyManager = _internalKeyManager ?? this;
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
internal XmlKeyManager(
|
||||
IOptions<KeyManagementOptions> keyManagementOptions,
|
||||
IActivator activator,
|
||||
ILoggerFactory loggerFactory,
|
||||
IInternalXmlKeyManager internalXmlKeyManager)
|
||||
: this(keyManagementOptions, activator, loggerFactory)
|
||||
{
|
||||
_internalKeyManager = internalXmlKeyManager;
|
||||
}
|
||||
|
||||
internal IXmlEncryptor KeyEncryptor { get; }
|
||||
|
|
@ -177,7 +170,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
else
|
||||
{
|
||||
// Skip unknown elements.
|
||||
_logger?.UnknownElementWithNameFoundInKeyringSkipping(element.Name);
|
||||
_logger.UnknownElementWithNameFoundInKeyringSkipping(element.Name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,11 +184,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
if (key != null)
|
||||
{
|
||||
key.SetRevoked();
|
||||
_logger?.MarkedKeyAsRevokedInTheKeyring(revokedKeyId);
|
||||
_logger.MarkedKeyAsRevokedInTheKeyring(revokedKeyId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.TriedToProcessRevocationOfKeyButNoSuchKeyWasFound(revokedKeyId);
|
||||
_logger.TriedToProcessRevocationOfKeyButNoSuchKeyWasFound(revokedKeyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +206,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
if (key.CreationDate < mostRecentMassRevocationDate)
|
||||
{
|
||||
key.SetRevoked();
|
||||
_logger?.MarkedKeyAsRevokedInTheKeyring(key.KeyId);
|
||||
_logger.MarkedKeyAsRevokedInTheKeyring(key.KeyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -239,14 +232,14 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
DateTimeOffset activationDate = (DateTimeOffset)keyElement.Element(ActivationDateElementName);
|
||||
DateTimeOffset expirationDate = (DateTimeOffset)keyElement.Element(ExpirationDateElementName);
|
||||
|
||||
_logger?.FoundKey(keyId);
|
||||
_logger.FoundKey(keyId);
|
||||
|
||||
return new DeferredKey(
|
||||
keyId: keyId,
|
||||
creationDate: creationDate,
|
||||
activationDate: activationDate,
|
||||
expirationDate: expirationDate,
|
||||
keyManager: _internalKeyManager,
|
||||
keyManager: this,
|
||||
keyElement: keyElement);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -270,14 +263,14 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
// this is a mass revocation of all keys as of the specified revocation date
|
||||
DateTimeOffset massRevocationDate = (DateTimeOffset)revocationElement.Element(RevocationDateElementName);
|
||||
_logger?.FoundRevocationOfAllKeysCreatedPriorTo(massRevocationDate);
|
||||
_logger.FoundRevocationOfAllKeysCreatedPriorTo(massRevocationDate);
|
||||
return massRevocationDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// only one key is being revoked
|
||||
var keyId = XmlConvert.ToGuid(keyIdAsString);
|
||||
_logger?.FoundRevocationOfKey(keyId);
|
||||
_logger.FoundRevocationOfKey(keyId);
|
||||
return keyId;
|
||||
}
|
||||
}
|
||||
|
|
@ -285,7 +278,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
// Any exceptions that occur are fatal - we don't want to continue if we cannot process
|
||||
// revocation information.
|
||||
_logger?.ExceptionWhileProcessingRevocationElement(revocationElement, ex);
|
||||
_logger.ExceptionWhileProcessingRevocationElement(revocationElement, ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -299,7 +292,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
// <reason>...</reason>
|
||||
// </revocation>
|
||||
|
||||
_logger?.RevokingAllKeysAsOfForReason(revocationDate, reason);
|
||||
_logger.RevokingAllKeysAsOfForReason(revocationDate, reason);
|
||||
|
||||
var revocationElement = new XElement(RevocationElementName,
|
||||
new XAttribute(VersionAttributeName, 1),
|
||||
|
|
@ -327,7 +320,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
{
|
||||
if (!suppressLogging)
|
||||
{
|
||||
_logger?.KeyCacheExpirationTokenTriggeredByOperation(opName);
|
||||
_logger.KeyCacheExpirationTokenTriggeredByOperation(opName);
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref _cacheExpirationTokenSource, new CancellationTokenSource())?.Cancel();
|
||||
|
|
@ -341,10 +334,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
// include sensitive information in the exception message.
|
||||
|
||||
// write sanitized <key> element
|
||||
_logger?.ExceptionWhileProcessingKeyElement(keyElement.WithoutChildNodes(), error);
|
||||
_logger.ExceptionWhileProcessingKeyElement(keyElement.WithoutChildNodes(), error);
|
||||
|
||||
// write full <key> element
|
||||
_logger?.AnExceptionOccurredWhileProcessingElementDebug(keyElement, error);
|
||||
_logger.AnExceptionOccurredWhileProcessingElementDebug(keyElement, error);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -359,13 +352,13 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
// </descriptor>
|
||||
// </key>
|
||||
|
||||
_logger?.CreatingKey(keyId, creationDate, activationDate, expirationDate);
|
||||
_logger.CreatingKey(keyId, creationDate, activationDate, expirationDate);
|
||||
|
||||
var newDescriptor = _authenticatedEncryptorConfiguration.CreateNewDescriptor()
|
||||
?? CryptoUtil.Fail<IAuthenticatedEncryptorDescriptor>("CreateNewDescriptor returned null.");
|
||||
var descriptorXmlInfo = newDescriptor.ExportToXml();
|
||||
|
||||
_logger?.DescriptorDeserializerTypeForKeyIs(keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName);
|
||||
_logger.DescriptorDeserializerTypeForKeyIs(keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName);
|
||||
|
||||
// build the <key> element
|
||||
var keyElement = new XElement(KeyElementName,
|
||||
|
|
@ -381,23 +374,23 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
// If key escrow policy is in effect, write the *unencrypted* key now.
|
||||
if (_keyEscrowSink != null)
|
||||
{
|
||||
_logger?.KeyEscrowSinkFoundWritingKeyToEscrow(keyId);
|
||||
_logger.KeyEscrowSinkFoundWritingKeyToEscrow(keyId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.NoKeyEscrowSinkFoundNotWritingKeyToEscrow(keyId);
|
||||
_logger.NoKeyEscrowSinkFoundNotWritingKeyToEscrow(keyId);
|
||||
}
|
||||
_keyEscrowSink?.Store(keyId, keyElement);
|
||||
|
||||
// If an XML encryptor has been configured, protect secret key material now.
|
||||
if (KeyEncryptor == null)
|
||||
{
|
||||
_logger?.NoXMLEncryptorConfiguredKeyMayBePersistedToStorageInUnencryptedForm(keyId);
|
||||
_logger.NoXMLEncryptorConfiguredKeyMayBePersistedToStorageInUnencryptedForm(keyId);
|
||||
}
|
||||
var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement;
|
||||
|
||||
// Persist it to the underlying repository and trigger the cancellation token.
|
||||
var friendlyName = Invariant($"key-{keyId:D}");
|
||||
var friendlyName = string.Format(CultureInfo.InvariantCulture, "key-{0:D}", keyId);
|
||||
KeyRepository.StoreElement(possiblyEncryptedKeyElement, friendlyName);
|
||||
TriggerAndResetCacheExpirationToken();
|
||||
|
||||
|
|
@ -440,7 +433,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
// <reason>...</reason>
|
||||
// </revocation>
|
||||
|
||||
_logger?.RevokingKeyForReason(keyId, revocationDate, reason);
|
||||
_logger.RevokingKeyForReason(keyId, revocationDate, reason);
|
||||
|
||||
var revocationElement = new XElement(RevocationElementName,
|
||||
new XAttribute(VersionAttributeName, 1),
|
||||
|
|
@ -450,9 +443,97 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
|||
new XElement(ReasonElementName, reason));
|
||||
|
||||
// Persist it to the underlying repository and trigger the cancellation token
|
||||
var friendlyName = Invariant($"revocation-{keyId:D}");
|
||||
var friendlyName = string.Format(CultureInfo.InvariantCulture, "revocation-{0:D}", keyId);
|
||||
KeyRepository.StoreElement(revocationElement, friendlyName);
|
||||
TriggerAndResetCacheExpirationToken();
|
||||
}
|
||||
|
||||
internal KeyValuePair<IXmlRepository, IXmlEncryptor> GetFallbackKeyRepositoryEncryptorPair()
|
||||
{
|
||||
IXmlRepository repository = null;
|
||||
IXmlEncryptor encryptor = null;
|
||||
|
||||
// If we're running in Azure Web Sites, the key repository goes in the %HOME% directory.
|
||||
var azureWebSitesKeysFolder = FileSystemXmlRepository.GetKeyStorageDirectoryForAzureWebSites();
|
||||
if (azureWebSitesKeysFolder != null)
|
||||
{
|
||||
_logger.UsingAzureAsKeyRepository(azureWebSitesKeysFolder.FullName);
|
||||
|
||||
// Cloud DPAPI isn't yet available, so we don't encrypt keys at rest.
|
||||
// This isn't all that different than what Azure Web Sites does today, and we can always add this later.
|
||||
repository = new FileSystemXmlRepository(azureWebSitesKeysFolder, _loggerFactory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the user profile is available, store keys in the user profile directory.
|
||||
var localAppDataKeysFolder = FileSystemXmlRepository.DefaultKeyStorageDirectory;
|
||||
if (localAppDataKeysFolder != null)
|
||||
{
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// If the user profile is available, we can protect using DPAPI.
|
||||
// Probe to see if protecting to local user is available, and use it as the default if so.
|
||||
encryptor = new DpapiXmlEncryptor(
|
||||
protectToLocalMachine: !DpapiSecretSerializerHelper.CanProtectToCurrentUserAccount(),
|
||||
loggerFactory: _loggerFactory);
|
||||
}
|
||||
repository = new FileSystemXmlRepository(localAppDataKeysFolder, _loggerFactory);
|
||||
|
||||
if (encryptor != null)
|
||||
{
|
||||
_logger.UsingProfileAsKeyRepositoryWithDPAPI(localAppDataKeysFolder.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.UsingProfileAsKeyRepository(localAppDataKeysFolder.FullName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use profile isn't available - can we use the HKLM registry?
|
||||
RegistryKey regKeyStorageKey = null;
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey;
|
||||
}
|
||||
if (regKeyStorageKey != null)
|
||||
{
|
||||
// If the user profile isn't available, we can protect using DPAPI (to machine).
|
||||
encryptor = new DpapiXmlEncryptor(protectToLocalMachine: true, loggerFactory: _loggerFactory);
|
||||
repository = new RegistryXmlRepository(regKeyStorageKey, _loggerFactory);
|
||||
|
||||
_logger.UsingRegistryAsKeyRepositoryWithDPAPI(regKeyStorageKey.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Final fallback - use an ephemeral repository since we don't know where else to go.
|
||||
// This can only be used for development scenarios.
|
||||
repository = new EphemeralXmlRepository(_loggerFactory);
|
||||
|
||||
_logger.UsingEphemeralKeyRepository();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new KeyValuePair<IXmlRepository, IXmlEncryptor>(repository, encryptor);
|
||||
}
|
||||
|
||||
private sealed class AggregateKeyEscrowSink : IKeyEscrowSink
|
||||
{
|
||||
private readonly IList<IKeyEscrowSink> _sinks;
|
||||
|
||||
public AggregateKeyEscrowSink(IList<IKeyEscrowSink> sinks)
|
||||
{
|
||||
_sinks = sinks;
|
||||
}
|
||||
|
||||
public void Store(Guid keyId, XElement element)
|
||||
{
|
||||
foreach (var sink in _sinks)
|
||||
{
|
||||
sink.Store(keyId, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,6 +394,22 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("LifetimeMustNotBeNegative"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' instance could not be found. When an '{1}' instance is set, a corresponding '{0}' instance must also be set.
|
||||
/// </summary>
|
||||
internal static string XmlKeyManager_IXmlRepositoryNotFound
|
||||
{
|
||||
get { return GetString("XmlKeyManager_IXmlRepositoryNotFound"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' instance could not be found. When an '{1}' instance is set, a corresponding '{0}' instance must also be set.
|
||||
/// </summary>
|
||||
internal static string FormatXmlKeyManager_IXmlRepositoryNotFound(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("XmlKeyManager_IXmlRepositoryNotFound"), p0, p1);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
private const string To = "Microsoft.AspNetCore.DataProtection";
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public RC1ForwardingActivator(IServiceProvider services) : this(services, null)
|
||||
public RC1ForwardingActivator(IServiceProvider services) : this(services, DataProtectionProviderFactory.GetDefaultLoggerFactory())
|
||||
{
|
||||
}
|
||||
|
||||
public RC1ForwardingActivator(IServiceProvider services, ILoggerFactory loggerFactory) : base(services)
|
||||
{
|
||||
_logger = loggerFactory?.CreateLogger(typeof(RC1ForwardingActivator));
|
||||
_logger = loggerFactory.CreateLogger(typeof(RC1ForwardingActivator));
|
||||
}
|
||||
|
||||
public override object CreateInstance(Type expectedBaseType, string implementationTypeName)
|
||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
var type = Type.GetType(forwardedImplementationTypeName, false);
|
||||
if (type != null)
|
||||
{
|
||||
_logger?.LogDebug("Forwarded activator type request from {FromType} to {ToType}",
|
||||
_logger.LogDebug("Forwarded activator type request from {FromType} to {ToType}",
|
||||
implementationTypeName,
|
||||
forwardedImplementationTypeName);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection
|
||||
{
|
||||
internal class RegistryPolicy
|
||||
{
|
||||
public RegistryPolicy(
|
||||
AlgorithmConfiguration configuration,
|
||||
IEnumerable<IKeyEscrowSink> keyEscrowSinks,
|
||||
int? defaultKeyLifetime)
|
||||
{
|
||||
EncryptorConfiguration = configuration;
|
||||
KeyEscrowSinks = keyEscrowSinks;
|
||||
DefaultKeyLifetime = defaultKeyLifetime;
|
||||
}
|
||||
|
||||
public AlgorithmConfiguration EncryptorConfiguration { get; }
|
||||
|
||||
public IEnumerable<IKeyEscrowSink> KeyEscrowSinks { get; }
|
||||
|
||||
public int? DefaultKeyLifetime { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,10 @@ using System.Globalization;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Internal;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection
|
||||
|
|
@ -19,11 +20,22 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
/// </summary>
|
||||
internal sealed class RegistryPolicyResolver
|
||||
{
|
||||
private readonly RegistryKey _policyRegKey;
|
||||
private readonly Func<RegistryKey> _getPolicyRegKey;
|
||||
private readonly IActivator _activator;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
internal RegistryPolicyResolver(RegistryKey policyRegKey)
|
||||
public RegistryPolicyResolver(IActivator activator, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_policyRegKey = policyRegKey;
|
||||
_getPolicyRegKey = () => Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection");
|
||||
_activator = activator;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
internal RegistryPolicyResolver(RegistryKey policyRegKey, IActivator activator, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_getPolicyRegKey = () => policyRegKey;
|
||||
_activator = activator;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
// populates an options object from values stored in the registry
|
||||
|
|
@ -59,11 +71,11 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
|
||||
private static List<string> ReadKeyEscrowSinks(RegistryKey key)
|
||||
{
|
||||
List<string> sinks = new List<string>();
|
||||
var sinks = new List<string>();
|
||||
|
||||
// The format of this key is "type1; type2; ...".
|
||||
// We call Type.GetType to perform an eager check that the type exists.
|
||||
string sinksFromRegistry = (string)key.GetValue("KeyEscrowSinks");
|
||||
var sinksFromRegistry = (string)key.GetValue("KeyEscrowSinks");
|
||||
if (sinksFromRegistry != null)
|
||||
{
|
||||
foreach (string sinkFromRegistry in sinksFromRegistry.Split(';'))
|
||||
|
|
@ -81,69 +93,60 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of <see cref="ServiceDescriptor"/>s from the default registry location.
|
||||
/// Returns a <see cref="RegistryPolicy"/> from the default registry location.
|
||||
/// </summary>
|
||||
public static ServiceDescriptor[] ResolveDefaultPolicy()
|
||||
public static RegistryPolicy ResolveDefaultPolicy(IActivator activator, ILoggerFactory loggerFactory)
|
||||
{
|
||||
var subKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection");
|
||||
if (subKey != null)
|
||||
return new RegistryPolicyResolver(activator, loggerFactory).ResolvePolicy();
|
||||
}
|
||||
|
||||
internal RegistryPolicy ResolvePolicy()
|
||||
{
|
||||
using (var registryKey = _getPolicyRegKey())
|
||||
{
|
||||
using (subKey)
|
||||
{
|
||||
return new RegistryPolicyResolver(subKey).ResolvePolicy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ServiceDescriptor[0];
|
||||
return ResolvePolicyCore(registryKey); // fully evaluate enumeration while the reg key is open
|
||||
}
|
||||
}
|
||||
|
||||
internal ServiceDescriptor[] ResolvePolicy()
|
||||
private RegistryPolicy ResolvePolicyCore(RegistryKey policyRegKey)
|
||||
{
|
||||
return ResolvePolicyCore().ToArray(); // fully evaluate enumeration while the reg key is open
|
||||
}
|
||||
if (policyRegKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private IEnumerable<ServiceDescriptor> ResolvePolicyCore()
|
||||
{
|
||||
// Read the encryption options type: CNG-CBC, CNG-GCM, Managed
|
||||
IInternalAuthenticatedEncryptionSettings options = null;
|
||||
string encryptionType = (string)_policyRegKey.GetValue("EncryptionType");
|
||||
AlgorithmConfiguration configuration = null;
|
||||
|
||||
var encryptionType = (string)policyRegKey.GetValue("EncryptionType");
|
||||
if (String.Equals(encryptionType, "CNG-CBC", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options = new CngCbcAuthenticatedEncryptionSettings();
|
||||
configuration = new CngCbcAuthenticatedEncryptorConfiguration();
|
||||
}
|
||||
else if (String.Equals(encryptionType, "CNG-GCM", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options = new CngGcmAuthenticatedEncryptionSettings();
|
||||
configuration = new CngGcmAuthenticatedEncryptorConfiguration();
|
||||
}
|
||||
else if (String.Equals(encryptionType, "Managed", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options = new ManagedAuthenticatedEncryptionSettings();
|
||||
configuration = new ManagedAuthenticatedEncryptorConfiguration();
|
||||
}
|
||||
else if (!String.IsNullOrEmpty(encryptionType))
|
||||
{
|
||||
throw CryptoUtil.Fail("Unrecognized EncryptionType: " + encryptionType);
|
||||
}
|
||||
if (options != null)
|
||||
if (configuration != null)
|
||||
{
|
||||
PopulateOptions(options, _policyRegKey);
|
||||
yield return DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(options);
|
||||
PopulateOptions(configuration, policyRegKey);
|
||||
}
|
||||
|
||||
// Read ancillary data
|
||||
|
||||
int? defaultKeyLifetime = (int?)_policyRegKey.GetValue("DefaultKeyLifetime");
|
||||
if (defaultKeyLifetime.HasValue)
|
||||
{
|
||||
yield return DataProtectionServiceDescriptors.ConfigureOptions_DefaultKeyLifetime(defaultKeyLifetime.Value);
|
||||
}
|
||||
var defaultKeyLifetime = (int?)policyRegKey.GetValue("DefaultKeyLifetime");
|
||||
|
||||
var keyEscrowSinks = ReadKeyEscrowSinks(_policyRegKey);
|
||||
foreach (var keyEscrowSink in keyEscrowSinks)
|
||||
{
|
||||
yield return DataProtectionServiceDescriptors.IKeyEscrowSink_FromTypeName(keyEscrowSink);
|
||||
}
|
||||
var keyEscrowSinks = ReadKeyEscrowSinks(policyRegKey).Select(item => _activator.CreateInstance<IKeyEscrowSink>(item));
|
||||
|
||||
return new RegistryPolicy(configuration, keyEscrowSinks, defaultKeyLifetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
{
|
||||
private readonly List<XElement> _storedElements = new List<XElement>();
|
||||
|
||||
public EphemeralXmlRepository(IServiceProvider services)
|
||||
public EphemeralXmlRepository(ILoggerFactory loggerFactory)
|
||||
{
|
||||
var logger = services?.GetLogger<EphemeralXmlRepository>();
|
||||
logger?.UsingInmemoryRepository();
|
||||
var logger = loggerFactory.CreateLogger<EphemeralXmlRepository>();
|
||||
logger.UsingInmemoryRepository();
|
||||
}
|
||||
|
||||
public virtual IReadOnlyCollection<XElement> GetAllElements()
|
||||
|
|
|
|||
|
|
@ -24,21 +24,8 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
/// Creates a <see cref="FileSystemXmlRepository"/> with keys stored at the given directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory in which to persist key material.</param>
|
||||
public FileSystemXmlRepository(DirectoryInfo directory)
|
||||
: this(directory, services: null)
|
||||
{
|
||||
if (directory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directory));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="FileSystemXmlRepository"/> with keys stored at the given directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory in which to persist key material.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param>
|
||||
public FileSystemXmlRepository(DirectoryInfo directory, IServiceProvider services)
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
public FileSystemXmlRepository(DirectoryInfo directory, ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (directory == null)
|
||||
{
|
||||
|
|
@ -46,8 +33,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
}
|
||||
|
||||
Directory = directory;
|
||||
Services = services;
|
||||
_logger = services?.GetLogger<FileSystemXmlRepository>();
|
||||
_logger = loggerFactory.CreateLogger<FileSystemXmlRepository>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -65,11 +51,6 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
/// </summary>
|
||||
public DirectoryInfo Directory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServiceProvider"/> provided to the constructor.
|
||||
/// </summary>
|
||||
protected IServiceProvider Services { get; }
|
||||
|
||||
private const string DataProtectionKeysFolderName = "DataProtection-Keys";
|
||||
|
||||
private static DirectoryInfo GetKeyStorageDirectoryFromBaseAppDataPath(string basePath)
|
||||
|
|
@ -185,7 +166,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
|
||||
private XElement ReadElementFromFile(string fullPath)
|
||||
{
|
||||
_logger?.ReadingDataFromFile(fullPath);
|
||||
_logger.ReadingDataFromFile(fullPath);
|
||||
|
||||
using (var fileStream = File.OpenRead(fullPath))
|
||||
{
|
||||
|
|
@ -203,7 +184,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
if (!IsSafeFilename(friendlyName))
|
||||
{
|
||||
var newFriendlyName = Guid.NewGuid().ToString();
|
||||
_logger?.NameIsNotSafeFileName(friendlyName, newFriendlyName);
|
||||
_logger.NameIsNotSafeFileName(friendlyName, newFriendlyName);
|
||||
friendlyName = newFriendlyName;
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +210,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
|
||||
// Once the file has been fully written, perform the rename.
|
||||
// Renames are atomic operations on the file systems we support.
|
||||
_logger?.WritingDataToFile(finalFilename);
|
||||
_logger.WritingDataToFile(finalFilename);
|
||||
File.Move(tempFilename, finalFilename);
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Win32;
|
||||
|
||||
using static System.FormattableString;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -26,21 +25,8 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
/// Creates a <see cref="RegistryXmlRepository"/> with keys stored in the given registry key.
|
||||
/// </summary>
|
||||
/// <param name="registryKey">The registry key in which to persist key material.</param>
|
||||
public RegistryXmlRepository(RegistryKey registryKey)
|
||||
: this(registryKey, services: null)
|
||||
{
|
||||
if (registryKey == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(registryKey));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RegistryXmlRepository"/> with keys stored in the given registry key.
|
||||
/// </summary>
|
||||
/// <param name="registryKey">The registry key in which to persist key material.</param>
|
||||
/// <param name="services">The <see cref="IServiceProvider"/> used to resolve services.</param>
|
||||
public RegistryXmlRepository(RegistryKey registryKey, IServiceProvider services)
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
public RegistryXmlRepository(RegistryKey registryKey, ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (registryKey == null)
|
||||
{
|
||||
|
|
@ -48,8 +34,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
}
|
||||
|
||||
RegistryKey = registryKey;
|
||||
Services = services;
|
||||
_logger = services?.GetLogger<RegistryXmlRepository>();
|
||||
_logger = loggerFactory.CreateLogger<RegistryXmlRepository>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -67,11 +52,6 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
/// </summary>
|
||||
public RegistryKey RegistryKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServiceProvider"/> provided to the constructor.
|
||||
/// </summary>
|
||||
protected IServiceProvider Services { get; }
|
||||
|
||||
public virtual IReadOnlyCollection<XElement> GetAllElements()
|
||||
{
|
||||
// forces complete enumeration
|
||||
|
|
@ -107,7 +87,11 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
// Even though this is in HKLM, WAS ensures that applications hosted in IIS are properly isolated.
|
||||
// See APP_POOL::EnsureSharedMachineKeyStorage in WAS source for more info.
|
||||
// The version number will need to change if IIS hosts Core CLR directly.
|
||||
var aspnetAutoGenKeysBaseKeyName = Invariant($@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{WindowsIdentity.GetCurrent().User.Value}");
|
||||
var aspnetAutoGenKeysBaseKeyName = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{0}",
|
||||
WindowsIdentity.GetCurrent().User.Value);
|
||||
|
||||
var aspnetBaseKey = hklmBaseKey.OpenSubKey(aspnetAutoGenKeysBaseKeyName, writable: true);
|
||||
if (aspnetBaseKey != null)
|
||||
{
|
||||
|
|
@ -141,7 +125,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
|
||||
private XElement ReadElementFromRegKey(RegistryKey regKey, string valueName)
|
||||
{
|
||||
_logger?.ReadingDataFromRegistryKeyValue(regKey, valueName);
|
||||
_logger.ReadingDataFromRegistryKeyValue(regKey, valueName);
|
||||
|
||||
var data = regKey.GetValue(valueName) as string;
|
||||
return (!String.IsNullOrEmpty(data)) ? XElement.Parse(data) : null;
|
||||
|
|
@ -157,7 +141,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
|||
if (!IsSafeRegistryValueName(friendlyName))
|
||||
{
|
||||
var newFriendlyName = Guid.NewGuid().ToString();
|
||||
_logger?.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
|
||||
_logger.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
|
||||
friendlyName = newFriendlyName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -189,4 +189,7 @@
|
|||
<data name="LifetimeMustNotBeNegative" xml:space="preserve">
|
||||
<value>{0} must not be negative</value>
|
||||
</data>
|
||||
<data name="XmlKeyManager_IXmlRepositoryNotFound" xml:space="preserve">
|
||||
<value>The '{0}' instance could not be found. When an '{1}' instance is set, a corresponding '{0}' instance must also be set.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
// 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 !NETSTANDARD1_3
|
||||
// These classes allow using the C# string interpolation feature from .NET 4.5.1.
|
||||
// They're slimmed-down versions of the classes that exist in .NET 4.6.
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal struct FormattableString
|
||||
{
|
||||
private readonly object[] _arguments;
|
||||
public readonly string Format;
|
||||
|
||||
internal FormattableString(string format, params object[] arguments)
|
||||
{
|
||||
Format = format;
|
||||
_arguments = arguments;
|
||||
}
|
||||
|
||||
public object[] GetArguments() => _arguments;
|
||||
|
||||
public static string Invariant(FormattableString formattable)
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, formattable.Format, formattable.GetArguments());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
internal static class FormattableStringFactory
|
||||
{
|
||||
public static FormattableString Create(string format, params object[] arguments)
|
||||
{
|
||||
return new FormattableString(format, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -9,7 +9,6 @@ using System.Security.Cryptography.Xml;
|
|||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||
|
|
@ -23,29 +22,13 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
private readonly IInternalCertificateXmlEncryptor _encryptor;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CertificateXmlEncryptor"/> given a certificate's thumbprint and an
|
||||
/// <see cref="ICertificateResolver"/> that can be used to resolve the certificate.
|
||||
/// </summary>
|
||||
/// <param name="thumbprint">The thumbprint (as a hex string) of the certificate with which to
|
||||
/// encrypt the key material. The certificate must be locatable by <paramref name="certificateResolver"/>.</param>
|
||||
/// <param name="certificateResolver">A resolver which can locate <see cref="X509Certificate2"/> objects.</param>
|
||||
public CertificateXmlEncryptor(string thumbprint, ICertificateResolver certificateResolver)
|
||||
: this(thumbprint, certificateResolver, services: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CertificateXmlEncryptor"/> given a certificate's thumbprint, an
|
||||
/// <see cref="ICertificateResolver"/> that can be used to resolve the certificate, and
|
||||
/// an <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="thumbprint">The thumbprint (as a hex string) of the certificate with which to
|
||||
/// encrypt the key material. The certificate must be locatable by <paramref name="certificateResolver"/>.</param>
|
||||
/// <param name="certificateResolver">A resolver which can locate <see cref="X509Certificate2"/> objects.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param>
|
||||
public CertificateXmlEncryptor(string thumbprint, ICertificateResolver certificateResolver, IServiceProvider services)
|
||||
: this(services)
|
||||
public CertificateXmlEncryptor(string thumbprint, ICertificateResolver certificateResolver, ILoggerFactory loggerFactory)
|
||||
: this(loggerFactory, encryptor: null)
|
||||
{
|
||||
if (thumbprint == null)
|
||||
{
|
||||
|
|
@ -60,23 +43,12 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
_certFactory = CreateCertFactory(thumbprint, certificateResolver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CertificateXmlEncryptor"/> given an <see cref="X509Certificate2"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="certificate">The <see cref="X509Certificate2"/> with which to encrypt the key material.</param>
|
||||
public CertificateXmlEncryptor(X509Certificate2 certificate)
|
||||
: this(certificate, services: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CertificateXmlEncryptor"/> given an <see cref="X509Certificate2"/> instance
|
||||
/// and an <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="certificate">The <see cref="X509Certificate2"/> with which to encrypt the key material.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param>
|
||||
public CertificateXmlEncryptor(X509Certificate2 certificate, IServiceProvider services)
|
||||
: this(services)
|
||||
public CertificateXmlEncryptor(X509Certificate2 certificate, ILoggerFactory loggerFactory)
|
||||
: this(loggerFactory, encryptor: null)
|
||||
{
|
||||
if (certificate == null)
|
||||
{
|
||||
|
|
@ -86,10 +58,10 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
_certFactory = () => certificate;
|
||||
}
|
||||
|
||||
internal CertificateXmlEncryptor(IServiceProvider services)
|
||||
internal CertificateXmlEncryptor(ILoggerFactory loggerFactory, IInternalCertificateXmlEncryptor encryptor)
|
||||
{
|
||||
_encryptor = services?.GetService<IInternalCertificateXmlEncryptor>() ?? this;
|
||||
_logger = services.GetLogger<CertificateXmlEncryptor>();
|
||||
_encryptor = encryptor ?? this;
|
||||
_logger = loggerFactory.CreateLogger<CertificateXmlEncryptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -149,7 +121,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.ExceptionWhileTryingToResolveCertificateWithThumbprint(thumbprint, ex);
|
||||
_logger.ExceptionWhileTryingToResolveCertificateWithThumbprint(thumbprint, ex);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
|
@ -161,7 +133,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
var cert = _certFactory()
|
||||
?? CryptoUtil.Fail<X509Certificate2>("Cert factory returned null.");
|
||||
|
||||
_logger?.EncryptingToX509CertificateWithThumbprint(cert.Thumbprint);
|
||||
_logger.EncryptingToX509CertificateWithThumbprint(cert.Thumbprint);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -169,7 +141,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.AnErrorOccurredWhileEncryptingToX509CertificateWithThumbprint(cert.Thumbprint, ex);
|
||||
_logger.AnErrorOccurredWhileEncryptingToX509CertificateWithThumbprint(cert.Thumbprint, ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Principal;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
|
|
@ -9,8 +10,6 @@ using Microsoft.AspNetCore.Cryptography.SafeHandles;
|
|||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using static System.FormattableString;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -29,18 +28,8 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
/// </summary>
|
||||
/// <param name="protectionDescriptorRule">The rule string from which to create the protection descriptor.</param>
|
||||
/// <param name="flags">Flags controlling the creation of the protection descriptor.</param>
|
||||
public DpapiNGXmlEncryptor(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags)
|
||||
: this(protectionDescriptorRule, flags, services: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="DpapiNGXmlEncryptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="protectionDescriptorRule">The rule string from which to create the protection descriptor.</param>
|
||||
/// <param name="flags">Flags controlling the creation of the protection descriptor.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param>
|
||||
public DpapiNGXmlEncryptor(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags, IServiceProvider services)
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
public DpapiNGXmlEncryptor(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags, ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (protectionDescriptorRule == null)
|
||||
{
|
||||
|
|
@ -53,7 +42,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
||||
CryptoUtil.AssertSafeHandleIsValid(_protectionDescriptorHandle);
|
||||
|
||||
_logger = services.GetLogger<DpapiNGXmlEncryptor>();
|
||||
_logger = loggerFactory.CreateLogger<DpapiNGXmlEncryptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -73,7 +62,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
}
|
||||
|
||||
var protectionDescriptorRuleString = _protectionDescriptorHandle.GetProtectionDescriptorRuleString();
|
||||
_logger?.EncryptingToWindowsDPAPINGUsingProtectionDescriptorRule(protectionDescriptorRuleString);
|
||||
_logger.EncryptingToWindowsDPAPINGUsingProtectionDescriptorRule(protectionDescriptorRuleString);
|
||||
|
||||
// Convert the XML element to a binary secret so that it can be run through DPAPI
|
||||
byte[] cngDpapiEncryptedData;
|
||||
|
|
@ -86,7 +75,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.ErrorOccurredWhileEncryptingToWindowsDPAPING(ex);
|
||||
_logger.ErrorOccurredWhileEncryptingToWindowsDPAPING(ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +107,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
using (var currentIdentity = WindowsIdentity.GetCurrent())
|
||||
{
|
||||
// use the SID to create an SDDL string
|
||||
return Invariant($"SID={currentIdentity.User.Value}");
|
||||
return string.Format(CultureInfo.InvariantCulture, "SID={0}", currentIdentity.User.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,28 +21,18 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
private readonly ILogger _logger;
|
||||
private readonly bool _protectToLocalMachine;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="DpapiXmlEncryptor"/> given a protection scope.
|
||||
/// </summary>
|
||||
/// <param name="protectToLocalMachine">'true' if the data should be decipherable by anybody on the local machine,
|
||||
/// 'false' if the data should only be decipherable by the current Windows user account.</param>
|
||||
public DpapiXmlEncryptor(bool protectToLocalMachine)
|
||||
: this(protectToLocalMachine, services: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="DpapiXmlEncryptor"/> given a protection scope and an <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="protectToLocalMachine">'true' if the data should be decipherable by anybody on the local machine,
|
||||
/// 'false' if the data should only be decipherable by the current Windows user account.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param>
|
||||
public DpapiXmlEncryptor(bool protectToLocalMachine, IServiceProvider services)
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
public DpapiXmlEncryptor(bool protectToLocalMachine, ILoggerFactory loggerFactory)
|
||||
{
|
||||
CryptoUtil.AssertPlatformIsWindows();
|
||||
|
||||
_protectToLocalMachine = protectToLocalMachine;
|
||||
_logger = services.GetLogger<DpapiXmlEncryptor>();
|
||||
_logger = loggerFactory.CreateLogger<DpapiXmlEncryptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -62,11 +52,11 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
}
|
||||
if (_protectToLocalMachine)
|
||||
{
|
||||
_logger?.EncryptingToWindowsDPAPIForLocalMachineAccount();
|
||||
_logger.EncryptingToWindowsDPAPIForLocalMachineAccount();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.EncryptingToWindowsDPAPIForCurrentUserAccount(WindowsIdentity.GetCurrent().Name);
|
||||
_logger.EncryptingToWindowsDPAPIForCurrentUserAccount(WindowsIdentity.GetCurrent().Name);
|
||||
}
|
||||
|
||||
// Convert the XML element to a binary secret so that it can be run through DPAPI
|
||||
|
|
@ -80,7 +70,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.ErrorOccurredWhileEncryptingToWindowsDPAPI(ex);
|
||||
_logger.ErrorOccurredWhileEncryptingToWindowsDPAPI(ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,4 +21,8 @@
|
|||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -21,4 +21,8 @@
|
|||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -21,4 +21,8 @@
|
|||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -20,4 +20,8 @@
|
|||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -21,4 +21,8 @@
|
|||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection.Extensions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -152,7 +153,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
public void RoundTrip_ProtectedData()
|
||||
{
|
||||
// Arrange
|
||||
var ephemeralProtector = new EphemeralDataProtectionProvider().CreateProtector("my purpose");
|
||||
var ephemeralProtector = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("my purpose");
|
||||
var timeLimitedProtector = new TimeLimitedDataProtector(ephemeralProtector);
|
||||
var expectedExpiration = StringToDateTime("2020-01-01 00:00:00Z");
|
||||
|
||||
|
|
|
|||
|
|
@ -21,4 +21,8 @@
|
|||
<PackageReference Include="xunit" Version="2.2.0-*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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 Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Test.Shared;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public class CngCbcAuthenticatedEncryptorFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateEncrptorInstance_UnknownDescriptorType_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var key = new Mock<IKey>();
|
||||
key.Setup(k => k.Descriptor).Returns(new Mock<IAuthenticatedEncryptorDescriptor>().Object);
|
||||
|
||||
var factory = new CngCbcAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
// Act
|
||||
var encryptor = factory.CreateEncryptorInstance(key.Object);
|
||||
|
||||
// Assert
|
||||
Assert.Null(encryptor);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[ConditionalRunTestOnlyOnWindows]
|
||||
public void CreateEncrptorInstance_ExpectedDescriptorType_ReturnsEncryptor()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new CngCbcAuthenticatedEncryptorConfiguration().CreateNewDescriptor();
|
||||
var key = new Mock<IKey>();
|
||||
key.Setup(k => k.Descriptor).Returns(descriptor);
|
||||
|
||||
var factory = new CngCbcAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
// Act
|
||||
var encryptor = factory.CreateEncryptorInstance(key.Object);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(encryptor);
|
||||
Assert.IsType<CbcAuthenticatedEncryptor>(encryptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Test.Shared;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public class CngGcmAuthenticatedEncryptorFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateEncrptorInstance_UnknownDescriptorType_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var key = new Mock<IKey>();
|
||||
key.Setup(k => k.Descriptor).Returns(new Mock<IAuthenticatedEncryptorDescriptor>().Object);
|
||||
|
||||
var factory = new CngGcmAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
// Act
|
||||
var encryptor = factory.CreateEncryptorInstance(key.Object);
|
||||
|
||||
// Assert
|
||||
Assert.Null(encryptor);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[ConditionalRunTestOnlyOnWindows]
|
||||
public void CreateEncrptorInstance_ExpectedDescriptorType_ReturnsEncryptor()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new CngGcmAuthenticatedEncryptorConfiguration().CreateNewDescriptor();
|
||||
var key = new Mock<IKey>();
|
||||
key.Setup(k => k.Descriptor).Returns(descriptor);
|
||||
|
||||
var factory = new CngGcmAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
// Act
|
||||
var encryptor = factory.CreateEncryptorInstance(key.Object);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(encryptor);
|
||||
Assert.IsType<GcmAuthenticatedEncryptor>(encryptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
|
|
@ -13,13 +15,14 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void ImportFromXml_Cbc_CreatesAppropriateDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var control = new AuthenticatedEncryptorDescriptor(
|
||||
new AuthenticatedEncryptionSettings()
|
||||
var descriptor = new AuthenticatedEncryptorDescriptor(
|
||||
new AuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = EncryptionAlgorithm.AES_192_CBC,
|
||||
ValidationAlgorithm = ValidationAlgorithm.HMACSHA512
|
||||
},
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
|
||||
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
|
||||
|
||||
const string xml = @"
|
||||
<encryptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
|
||||
|
|
@ -27,7 +30,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
<validation algorithm='HMACSHA512' />
|
||||
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
|
||||
</encryptor>";
|
||||
var test = new AuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
|
||||
var deserializedDescriptor = new AuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
|
||||
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as AuthenticatedEncryptorDescriptor);
|
||||
|
||||
// Act & assert
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -36,5 +40,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
|
||||
Assert.Equal(plaintext, roundTripPlaintext);
|
||||
}
|
||||
|
||||
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(AuthenticatedEncryptorDescriptor descriptor)
|
||||
{
|
||||
var key = new Key(
|
||||
Guid.NewGuid(),
|
||||
DateTimeOffset.Now,
|
||||
DateTimeOffset.Now + TimeSpan.FromHours(1),
|
||||
DateTimeOffset.Now + TimeSpan.FromDays(30),
|
||||
descriptor);
|
||||
|
||||
var encryptorFactory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
return encryptorFactory.CreateEncryptorInstance(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ using System.Text.RegularExpressions;
|
|||
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||
using Microsoft.AspNetCore.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Managed;
|
||||
using Microsoft.AspNetCore.DataProtection.Test.Shared;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
|
|
@ -38,7 +40,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8),
|
||||
hmacAlgorithmHandle: BCryptAlgorithmHandle.OpenAlgorithmHandle(hashAlgorithm, hmac: true));
|
||||
var test = CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey).CreateEncryptorInstance();
|
||||
var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey));
|
||||
|
||||
// Act & assert - data round trips properly from control to test
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -64,7 +66,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
keyDerivationKey: masterKey,
|
||||
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_GCM,
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8));
|
||||
var test = CreateDescriptor(encryptionAlgorithm, ValidationAlgorithm.HMACSHA256 /* unused */, masterKey).CreateEncryptorInstance();
|
||||
var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, ValidationAlgorithm.HMACSHA256 /* unused */, masterKey));
|
||||
|
||||
// Act & assert - data round trips properly from control to test
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -102,7 +104,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
symmetricAlgorithmFactory: () => Aes.Create(),
|
||||
symmetricAlgorithmKeySizeInBytes: keyLengthInBits / 8,
|
||||
validationAlgorithmFactory: validationAlgorithmFactory);
|
||||
var test = CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey).CreateEncryptorInstance();
|
||||
var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey));
|
||||
|
||||
// Act & assert - data round trips properly from control to test
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -160,11 +162,26 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
|
||||
private static AuthenticatedEncryptorDescriptor CreateDescriptor(EncryptionAlgorithm encryptionAlgorithm, ValidationAlgorithm validationAlgorithm, ISecret masterKey)
|
||||
{
|
||||
return new AuthenticatedEncryptorDescriptor(new AuthenticatedEncryptionSettings()
|
||||
return new AuthenticatedEncryptorDescriptor(new AuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = encryptionAlgorithm,
|
||||
ValidationAlgorithm = validationAlgorithm
|
||||
}, masterKey);
|
||||
}
|
||||
|
||||
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(AuthenticatedEncryptorDescriptor descriptor)
|
||||
{
|
||||
// Dummy key with the specified descriptor.
|
||||
var key = new Key(
|
||||
Guid.NewGuid(),
|
||||
DateTimeOffset.Now,
|
||||
DateTimeOffset.Now + TimeSpan.FromHours(1),
|
||||
DateTimeOffset.Now + TimeSpan.FromDays(30),
|
||||
descriptor);
|
||||
|
||||
var encryptorFactory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
return encryptorFactory.CreateEncryptorInstance(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new CngCbcAuthenticatedEncryptorConfiguration(new CngCbcAuthenticatedEncryptionSettings());
|
||||
var configuration = new CngCbcAuthenticatedEncryptorConfiguration();
|
||||
|
||||
// Act
|
||||
var masterKey1 = ((CngCbcAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey;
|
||||
|
|
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void CreateNewDescriptor_PropagatesOptions()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new CngCbcAuthenticatedEncryptorConfiguration(new CngCbcAuthenticatedEncryptionSettings());
|
||||
var configuration = new CngCbcAuthenticatedEncryptorConfiguration();
|
||||
|
||||
// Act
|
||||
var descriptor = (CngCbcAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(configuration.Settings, descriptor.Settings);
|
||||
Assert.Equal(configuration, descriptor.Configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Test.Shared;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
|
|
@ -17,8 +19,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void ImportFromXml_CreatesAppropriateDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var control = new CngCbcAuthenticatedEncryptorDescriptor(
|
||||
new CngCbcAuthenticatedEncryptionSettings()
|
||||
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(
|
||||
new CngCbcAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM,
|
||||
EncryptionAlgorithmKeySize = 192,
|
||||
|
|
@ -26,7 +28,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
HashAlgorithm = Constants.BCRYPT_SHA512_ALGORITHM,
|
||||
HashAlgorithmProvider = null
|
||||
},
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
|
||||
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
|
||||
|
||||
const string xml = @"
|
||||
<descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
|
||||
|
|
@ -34,7 +37,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
<hash algorithm='SHA512' />
|
||||
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
|
||||
</descriptor>";
|
||||
var test = new CngCbcAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
|
||||
var deserializedDescriptor = new CngCbcAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
|
||||
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as CngCbcAuthenticatedEncryptorDescriptor);
|
||||
|
||||
// Act & assert
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -43,5 +47,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
|
||||
Assert.Equal(plaintext, roundTripPlaintext);
|
||||
}
|
||||
|
||||
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(CngCbcAuthenticatedEncryptorDescriptor descriptor)
|
||||
{
|
||||
var key = new Key(
|
||||
Guid.NewGuid(),
|
||||
DateTimeOffset.Now,
|
||||
DateTimeOffset.Now + TimeSpan.FromHours(1),
|
||||
DateTimeOffset.Now + TimeSpan.FromDays(30),
|
||||
descriptor);
|
||||
|
||||
var encryptorFactory = new CngCbcAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
return encryptorFactory.CreateEncryptorInstance(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
// Arrange
|
||||
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
|
||||
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptionSettings()
|
||||
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = "enc-alg",
|
||||
EncryptionAlgorithmKeySize = 2048,
|
||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
// Arrange
|
||||
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
|
||||
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptionSettings()
|
||||
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = "enc-alg",
|
||||
EncryptionAlgorithmKeySize = 2048,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new CngGcmAuthenticatedEncryptorConfiguration(new CngGcmAuthenticatedEncryptionSettings());
|
||||
var configuration = new CngGcmAuthenticatedEncryptorConfiguration();
|
||||
|
||||
// Act
|
||||
var masterKey1 = ((CngGcmAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey;
|
||||
|
|
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void CreateNewDescriptor_PropagatesOptions()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new CngGcmAuthenticatedEncryptorConfiguration(new CngGcmAuthenticatedEncryptionSettings());
|
||||
var configuration = new CngGcmAuthenticatedEncryptorConfiguration();
|
||||
|
||||
// Act
|
||||
var descriptor = (CngGcmAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(configuration.Settings, descriptor.Settings);
|
||||
Assert.Equal(configuration, descriptor.Configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Cryptography;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Test.Shared;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
|
|
@ -17,21 +19,23 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void ImportFromXml_CreatesAppropriateDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var control = new CngGcmAuthenticatedEncryptorDescriptor(
|
||||
new CngGcmAuthenticatedEncryptionSettings()
|
||||
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(
|
||||
new CngGcmAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM,
|
||||
EncryptionAlgorithmKeySize = 192,
|
||||
EncryptionAlgorithmProvider = null
|
||||
},
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
|
||||
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
|
||||
|
||||
const string xml = @"
|
||||
<descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
|
||||
<encryption algorithm='AES' keyLength='192' />
|
||||
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
|
||||
</descriptor>";
|
||||
var test = new CngGcmAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
|
||||
var deserializedDescriptor = new CngGcmAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
|
||||
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as CngGcmAuthenticatedEncryptorDescriptor);
|
||||
|
||||
// Act & assert
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -40,5 +44,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
|
||||
Assert.Equal(plaintext, roundTripPlaintext);
|
||||
}
|
||||
|
||||
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(CngGcmAuthenticatedEncryptorDescriptor descriptor)
|
||||
{
|
||||
var key = new Key(
|
||||
keyId: Guid.NewGuid(),
|
||||
creationDate: DateTimeOffset.Now,
|
||||
activationDate: DateTimeOffset.Now + TimeSpan.FromHours(1),
|
||||
expirationDate: DateTimeOffset.Now + TimeSpan.FromDays(30),
|
||||
descriptor: descriptor);
|
||||
|
||||
var encryptorFactory = new CngGcmAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
return encryptorFactory.CreateEncryptorInstance(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
// Arrange
|
||||
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
|
||||
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptionSettings()
|
||||
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = "enc-alg",
|
||||
EncryptionAlgorithmKeySize = 2048,
|
||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
// Arrange
|
||||
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
|
||||
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptionSettings()
|
||||
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithm = "enc-alg",
|
||||
EncryptionAlgorithmKeySize = 2048
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ManagedAuthenticatedEncryptorConfiguration(new ManagedAuthenticatedEncryptionSettings());
|
||||
var configuration = new ManagedAuthenticatedEncryptorConfiguration();
|
||||
|
||||
// Act
|
||||
var masterKey1 = ((ManagedAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey;
|
||||
|
|
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void CreateNewDescriptor_PropagatesOptions()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ManagedAuthenticatedEncryptorConfiguration(new ManagedAuthenticatedEncryptionSettings());
|
||||
var configuration = new ManagedAuthenticatedEncryptorConfiguration();
|
||||
|
||||
// Act
|
||||
var descriptor = (ManagedAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(configuration.Settings, descriptor.Settings);
|
||||
Assert.Equal(configuration, descriptor.Configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
|
|
@ -18,16 +20,17 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void ImportFromXml_BuiltInTypes_CreatesAppropriateDescriptor(Type encryptionAlgorithmType, Type validationAlgorithmType)
|
||||
{
|
||||
// Arrange
|
||||
var control = new ManagedAuthenticatedEncryptorDescriptor(
|
||||
new ManagedAuthenticatedEncryptionSettings()
|
||||
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
|
||||
new ManagedAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithmType = encryptionAlgorithmType,
|
||||
EncryptionAlgorithmKeySize = 192,
|
||||
ValidationAlgorithmType = validationAlgorithmType
|
||||
},
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
|
||||
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
|
||||
|
||||
string xml = String.Format(@"
|
||||
string xml = string.Format(@"
|
||||
<descriptor>
|
||||
<encryption algorithm='{0}' keyLength='192' />
|
||||
<validation algorithm='{1}' />
|
||||
|
|
@ -36,7 +39,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
</masterKey>
|
||||
</descriptor>",
|
||||
encryptionAlgorithmType.Name, validationAlgorithmType.Name);
|
||||
var test = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
|
||||
var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
|
||||
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as ManagedAuthenticatedEncryptorDescriptor);
|
||||
|
||||
// Act & assert
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -50,16 +54,17 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
public void ImportFromXml_CustomType_CreatesAppropriateDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var control = new ManagedAuthenticatedEncryptorDescriptor(
|
||||
new ManagedAuthenticatedEncryptionSettings()
|
||||
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
|
||||
new ManagedAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithmType = typeof(Aes),
|
||||
EncryptionAlgorithmKeySize = 192,
|
||||
ValidationAlgorithmType = typeof(HMACSHA384)
|
||||
},
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
|
||||
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
|
||||
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
|
||||
|
||||
string xml = String.Format(@"
|
||||
string xml = string.Format(@"
|
||||
<descriptor>
|
||||
<encryption algorithm='{0}' keyLength='192' />
|
||||
<validation algorithm='{1}' />
|
||||
|
|
@ -68,7 +73,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
</masterKey>
|
||||
</descriptor>",
|
||||
typeof(Aes).AssemblyQualifiedName, typeof(HMACSHA384).AssemblyQualifiedName);
|
||||
var test = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
|
||||
var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
|
||||
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as ManagedAuthenticatedEncryptorDescriptor);
|
||||
|
||||
// Act & assert
|
||||
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
|
@ -77,5 +83,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
|
||||
Assert.Equal(plaintext, roundTripPlaintext);
|
||||
}
|
||||
|
||||
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(ManagedAuthenticatedEncryptorDescriptor descriptor)
|
||||
{
|
||||
var key = new Key(
|
||||
Guid.NewGuid(),
|
||||
DateTimeOffset.Now,
|
||||
DateTimeOffset.Now + TimeSpan.FromHours(1),
|
||||
DateTimeOffset.Now + TimeSpan.FromDays(30),
|
||||
descriptor);
|
||||
|
||||
var encryptorFactory = new ManagedAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
return encryptorFactory.CreateEncryptorInstance(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
// Arrange
|
||||
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
|
||||
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptionSettings()
|
||||
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithmType = typeof(MySymmetricAlgorithm),
|
||||
EncryptionAlgorithmKeySize = 2048,
|
||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
|
||||
// Assert
|
||||
Assert.Equal(typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer), retVal.DeserializerType);
|
||||
string expectedXml = String.Format(@"
|
||||
string expectedXml = string.Format(@"
|
||||
<descriptor>
|
||||
<encryption algorithm='{0}' keyLength='2048' />
|
||||
<validation algorithm='{1}' />
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
{
|
||||
// Arrange
|
||||
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
|
||||
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptionSettings()
|
||||
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptorConfiguration()
|
||||
{
|
||||
EncryptionAlgorithmType = encryptionAlgorithmType,
|
||||
EncryptionAlgorithmKeySize = 2048,
|
||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
|||
|
||||
// Assert
|
||||
Assert.Equal(typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer), retVal.DeserializerType);
|
||||
string expectedXml = String.Format(@"
|
||||
string expectedXml = string.Format(@"
|
||||
<descriptor>
|
||||
<encryption algorithm='{0}' keyLength='2048' />
|
||||
<validation algorithm='{1}' />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// 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.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.DataProtection.Managed;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public class ManagedAuthenticatedEncryptorFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateEncrptorInstance_UnknownDescriptorType_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var key = new Mock<IKey>();
|
||||
key.Setup(k => k.Descriptor).Returns(new Mock<IAuthenticatedEncryptorDescriptor>().Object);
|
||||
|
||||
var factory = new ManagedAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
// Act
|
||||
var encryptor = factory.CreateEncryptorInstance(key.Object);
|
||||
|
||||
// Assert
|
||||
Assert.Null(encryptor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateEncrptorInstance_ExpectedDescriptorType_ReturnsEncryptor()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new ManagedAuthenticatedEncryptorConfiguration().CreateNewDescriptor();
|
||||
var key = new Mock<IKey>();
|
||||
key.Setup(k => k.Descriptor).Returns(descriptor);
|
||||
|
||||
var factory = new ManagedAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
|
||||
|
||||
// Act
|
||||
var encryptor = factory.CreateEncryptorInstance(key.Object);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(encryptor);
|
||||
Assert.IsType<ManagedAuthenticatedEncryptor>(encryptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.DataProtection
|
||||
|
|
@ -14,8 +15,8 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
public void DifferentProvider_SamePurpose_DoesNotRoundTripData()
|
||||
{
|
||||
// Arrange
|
||||
var dataProtector1 = new EphemeralDataProtectionProvider().CreateProtector("purpose");
|
||||
var dataProtector2 = new EphemeralDataProtectionProvider().CreateProtector("purpose");
|
||||
var dataProtector1 = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("purpose");
|
||||
var dataProtector2 = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("purpose");
|
||||
byte[] bytes = Encoding.UTF8.GetBytes("Hello there!");
|
||||
|
||||
// Act & assert
|
||||
|
|
@ -31,7 +32,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
public void SingleProvider_DifferentPurpose_DoesNotRoundTripData()
|
||||
{
|
||||
// Arrange
|
||||
var dataProtectionProvider = new EphemeralDataProtectionProvider();
|
||||
var dataProtectionProvider = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance);
|
||||
var dataProtector1 = dataProtectionProvider.CreateProtector("purpose");
|
||||
var dataProtector2 = dataProtectionProvider.CreateProtector("different purpose");
|
||||
byte[] bytes = Encoding.UTF8.GetBytes("Hello there!");
|
||||
|
|
@ -48,7 +49,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
|||
public void SingleProvider_SamePurpose_RoundTripsData()
|
||||
{
|
||||
// Arrange
|
||||
var dataProtectionProvider = new EphemeralDataProtectionProvider();
|
||||
var dataProtectionProvider = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance);
|
||||
var dataProtector1 = dataProtectionProvider.CreateProtector("purpose");
|
||||
var dataProtector2 = dataProtectionProvider.CreateProtector("purpose"); // should be equivalent to the previous instance
|
||||
byte[] bytes = Encoding.UTF8.GetBytes("Hello there!");
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue