[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:
Ajay Bhargav Baaskaran 2016-08-09 11:10:42 -07:00
parent bf7a238b85
commit cde3b96aa7
121 changed files with 2952 additions and 2185 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@ project.lock.json
.vs .vs
.build/ .build/
.testPublish/ .testPublish/
samples/**/temp-keys/

View File

@ -1,51 +1,57 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26127.0 VisualStudioVersion = 15.0.26206.0
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}"
EndProject 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}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{60336AB3-948D-4D15-A5FB-F32A2B91E814}"
EndProject 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}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5A3A5DE3-49AD-431C-971D-B01B62D94AE2}"
EndProject 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}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E1D86B1B-41D8-43C9-97FD-C2BF65C414E2}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
NuGet.config = NuGet.config NuGet.config = NuGet.config
EndProjectSection EndProjectSection
EndProject 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 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|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.ActiveCfg = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|x86.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -209,5 +239,8 @@ Global
{ABCF00E5-5B2F-469C-90DC-908C5A04C08D} = {60336AB3-948D-4D15-A5FB-F32A2B91E814} {ABCF00E5-5B2F-469C-90DC-908C5A04C08D} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{8C41240E-48F8-402F-9388-74CFE27F4D76} = {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} {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 EndGlobalSection
EndGlobal EndGlobal

View File

@ -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.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace CustomEncryptorSample
{
public static class CustomBuilderExtensions
{
public static IDataProtectionBuilder UseXmlEncryptor(
this IDataProtectionBuilder builder,
Func<IServiceProvider, IXmlEncryptor> factory)
{
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(serviceProvider =>
{
var instance = factory(serviceProvider);
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.XmlEncryptor = instance;
});
});
return builder;
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.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>

View File

@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CustomEncryptorSample
{
public class CustomXmlDecryptor : IXmlDecryptor
{
private readonly ILogger _logger;
public CustomXmlDecryptor(IServiceProvider services)
{
_logger = services.GetRequiredService<ILoggerFactory>().CreateLogger<CustomXmlDecryptor>();
}
public XElement Decrypt(XElement encryptedElement)
{
if (encryptedElement == null)
{
throw new ArgumentNullException(nameof(encryptedElement));
}
return new XElement(encryptedElement.Elements().Single());
}
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CustomEncryptorSample
{
public class CustomXmlEncryptor : IXmlEncryptor
{
private readonly ILogger _logger;
public CustomXmlEncryptor(IServiceProvider services)
{
_logger = services.GetRequiredService<ILoggerFactory>().CreateLogger<CustomXmlEncryptor>();
}
public EncryptedXmlInfo Encrypt(XElement plaintextElement)
{
if (plaintextElement == null)
{
throw new ArgumentNullException(nameof(plaintextElement));
}
_logger.LogInformation("Not encrypting key");
var newElement = new XElement("unencryptedKey",
new XComment(" This key is not encrypted. "),
new XElement(plaintextElement));
var encryptedTextElement = new EncryptedXmlInfo(newElement, typeof(CustomXmlDecryptor));
return encryptedTextElement;
}
}
}

View File

@ -0,0 +1,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}");
}
}
}

View File

@ -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"
}
}
}

View File

@ -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>

View File

@ -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}");
}
}
}
}

View File

@ -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"
}
}
}

View File

@ -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>

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
namespace NonDISample
{
public class Program
{
public static void Main(string[] args)
{
var keysFolder = Path.Combine(Directory.GetCurrentDirectory(), "temp-keys");
// instantiate the data protection system at this folder
var dataProtectionProvider = DataProtectionProvider.Create(
new DirectoryInfo(keysFolder),
configuration =>
{
configuration.SetApplicationName("my app name");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
configuration.ProtectKeysWithDpapi();
}
});
var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
// protect the payload
var protectedPayload = protector.Protect("Hello World!");
Console.WriteLine($"Protect returned: {protectedPayload}");
// unprotect the payload
var unprotectedPayload = protector.Unprotect(protectedPayload);
Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
}
}
}

View File

@ -0,0 +1,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"
}
}
}

View File

@ -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.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -6,103 +6,90 @@ using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.Cryptography.Cng; using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
{ {
/// <summary> public sealed class AuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory
/// Settings for configuring authenticated encryption algorithms.
/// </summary>
public sealed class AuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
{ {
/// <summary> private readonly ILoggerFactory _loggerFactory;
/// 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;
/// <summary> public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory)
/// 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()
{ {
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly. _loggerFactory = loggerFactory;
var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8));
try
{
encryptor.PerformSelfTest();
}
finally
{
(encryptor as IDisposable)?.Dispose();
}
} }
/* public IAuthenticatedEncryptor CreateEncryptorInstance(IKey key)
* HELPER ROUTINES
*/
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, IServiceProvider services = null)
{ {
return CreateImplementationOptions() var descriptor = key.Descriptor as AuthenticatedEncryptorDescriptor;
.ToConfiguration(services) if (descriptor == null)
.CreateDescriptorFromSecret(secret) {
.CreateEncryptorInstance(); 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. // GCM requires CNG, and CNG is only supported on Windows.
if (!OSVersionUtil.IsWindows()) if (!OSVersionUtil.IsWindows())
{ {
throw new PlatformNotSupportedException(Resources.Platform_WindowsRequiredForGcm); throw new PlatformNotSupportedException(Resources.Platform_WindowsRequiredForGcm);
} }
return new CngGcmAuthenticatedEncryptionSettings()
var configuration = new CngGcmAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm), EncryptionAlgorithm = GetBCryptAlgorithmNameFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm) EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm)
}; };
return new CngGcmAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
} }
else else
{ {
if (OSVersionUtil.IsWindows()) if (OSVersionUtil.IsWindows())
{ {
// CNG preferred over managed implementations if running on Windows // CNG preferred over managed implementations if running on Windows
return new CngCbcAuthenticatedEncryptionSettings() var configuration = new CngCbcAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm), EncryptionAlgorithm = GetBCryptAlgorithmNameFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm), EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm),
HashAlgorithm = GetBCryptAlgorithmName(ValidationAlgorithm) HashAlgorithm = GetBCryptAlgorithmNameFromValidationAlgorithm(authenticatedConfiguration.ValidationAlgorithm)
}; };
return new CngCbcAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
} }
else else
{ {
// Use managed implementations as a fallback // Use managed implementations as a fallback
return new ManagedAuthenticatedEncryptionSettings() var configuration = new ManagedAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithmType = GetManagedTypeForAlgorithm(EncryptionAlgorithm), EncryptionAlgorithmType = GetManagedTypeFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm), EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm),
ValidationAlgorithmType = GetManagedTypeForAlgorithm(ValidationAlgorithm) 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) private static int GetAlgorithmKeySizeInBits(EncryptionAlgorithm algorithm)
{ {
switch (algorithm) switch (algorithm)
@ -120,11 +107,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return 256; return 256;
default: 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) switch (algorithm)
{ {
@ -137,11 +124,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return Constants.BCRYPT_AES_ALGORITHM; return Constants.BCRYPT_AES_ALGORITHM;
default: 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) switch (algorithm)
{ {
@ -152,11 +139,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return Constants.BCRYPT_SHA512_ALGORITHM; return Constants.BCRYPT_SHA512_ALGORITHM;
default: 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) switch (algorithm)
{ {
@ -169,11 +156,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return typeof(Aes); return typeof(Aes);
default: 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) switch (algorithm)
{ {
@ -184,18 +171,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return typeof(HMACSHA512); return typeof(HMACSHA512);
default: 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);
}
} }
} }

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
/// <summary> public abstract class AlgorithmConfiguration
/// The basic configuration that serves as a factory for types related to authenticated encryption.
/// </summary>
public interface IAuthenticatedEncryptorConfiguration
{ {
internal const int KDK_SIZE_IN_BYTES = 512 / 8;
/// <summary> /// <summary>
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance based on this /// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance based on this
/// configuration. The newly-created instance contains unique key material and is distinct /// configuration. The newly-created instance contains unique key material and is distinct
/// from all other descriptors created by the <see cref="CreateNewDescriptor"/> method. /// from all other descriptors created by the <see cref="CreateNewDescriptor"/> method.
/// </summary> /// </summary>
/// <returns>A unique <see cref="IAuthenticatedEncryptorDescriptor"/>.</returns> /// <returns>A unique <see cref="IAuthenticatedEncryptorDescriptor"/>.</returns>
IAuthenticatedEncryptorDescriptor CreateNewDescriptor(); public abstract IAuthenticatedEncryptorDescriptor CreateNewDescriptor();
} }
} }

View File

@ -2,42 +2,57 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
/// <summary> /// <summary>
/// Represents a generalized authenticated encryption mechanism. /// Represents a generalized authenticated encryption mechanism.
/// </summary> /// </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) /// <summary>
: this(settings, services: null) /// 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);
} }
} }
} }

View File

@ -8,22 +8,15 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
/// <summary> /// <summary>
/// A descriptor which can create an authenticated encryption system based upon the /// 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> /// </summary>
public sealed class AuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor public sealed class AuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
{ {
private readonly IServiceProvider _services; public AuthenticatedEncryptorDescriptor(AuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
public AuthenticatedEncryptorDescriptor(AuthenticatedEncryptionSettings settings, ISecret masterKey)
: this(settings, masterKey, services: null)
{ {
} if (configuration == null)
public AuthenticatedEncryptorDescriptor(AuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
{
if (settings == null)
{ {
throw new ArgumentNullException(nameof(settings)); throw new ArgumentNullException(nameof(configuration));
} }
if (masterKey == null) if (masterKey == null)
@ -31,19 +24,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
throw new ArgumentNullException(nameof(masterKey)); throw new ArgumentNullException(nameof(masterKey));
} }
Settings = settings; Configuration = configuration;
MasterKey = masterKey; MasterKey = masterKey;
_services = services;
} }
internal ISecret MasterKey { get; } internal ISecret MasterKey { get; }
internal AuthenticatedEncryptionSettings Settings { get; } internal AuthenticatedEncryptorConfiguration Configuration { get; }
public IAuthenticatedEncryptor CreateEncryptorInstance()
{
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _services);
}
public XmlSerializedDescriptorInfo ExportToXml() public XmlSerializedDescriptorInfo ExportToXml()
{ {
@ -54,12 +41,12 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// </descriptor> // </descriptor>
var encryptionElement = new XElement("encryption", 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 XComment(" AES-GCM includes a 128-bit authentication tag, no extra validation algorithm required. ")
: (object)new XElement("validation", : (object)new XElement("validation",
new XAttribute("algorithm", Settings.ValidationAlgorithm)); new XAttribute("algorithm", Configuration.ValidationAlgorithm));
var outerElement = new XElement("descriptor", var outerElement = new XElement("descriptor",
encryptionElement, encryptionElement,

View File

@ -13,18 +13,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// </summary> /// </summary>
public sealed class AuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer public sealed class AuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
{ {
private readonly IServiceProvider _services;
public AuthenticatedEncryptorDescriptorDeserializer()
: this(services: null)
{
}
public AuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
{
_services = services;
}
/// <summary> /// <summary>
/// Imports the <see cref="AuthenticatedEncryptorDescriptor"/> from serialized XML. /// Imports the <see cref="AuthenticatedEncryptorDescriptor"/> from serialized XML.
/// </summary> /// </summary>
@ -41,20 +29,20 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// <masterKey requiresEncryption="true">...</masterKey> // <masterKey requiresEncryption="true">...</masterKey>
// </descriptor> // </descriptor>
var settings = new AuthenticatedEncryptionSettings(); var configuration = new AuthenticatedEncryptorConfiguration();
var encryptionElement = element.Element("encryption"); 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 // only read <validation> if not GCM
if (!AuthenticatedEncryptionSettings.IsGcmAlgorithm(settings.EncryptionAlgorithm)) if (!AuthenticatedEncryptorFactory.IsGcmAlgorithm(configuration.EncryptionAlgorithm))
{ {
var validationElement = element.Element("validation"); 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(); Secret masterKey = ((string)element.Elements("masterKey").Single()).ToSecret();
return new AuthenticatedEncryptorDescriptor(settings, masterKey, _services); return new AuthenticatedEncryptorDescriptor(configuration, masterKey);
} }
} }
} }

View File

@ -1,7 +1,7 @@
// 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. // 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 namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
@ -9,36 +9,91 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// Represents a configured authenticated encryption mechanism which uses /// Represents a configured authenticated encryption mechanism which uses
/// Windows CNG algorithms in CBC encryption + HMAC authentication modes. /// Windows CNG algorithms in CBC encryption + HMAC authentication modes.
/// </summary> /// </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) /// <summary>
: this(settings, services: null) /// 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);
} }
} }
} }

View File

@ -3,28 +3,20 @@
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
/// <summary> /// <summary>
/// A descriptor which can create an authenticated encryption system based upon the /// 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> /// </summary>
public sealed class CngCbcAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor public sealed class CngCbcAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
{ {
private readonly ILogger _log; public CngCbcAuthenticatedEncryptorDescriptor(CngCbcAuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
public CngCbcAuthenticatedEncryptorDescriptor(CngCbcAuthenticatedEncryptionSettings settings, ISecret masterKey)
: this(settings, masterKey, services: null)
{ {
} if (configuration == null)
public CngCbcAuthenticatedEncryptorDescriptor(CngCbcAuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
{
if (settings == null)
{ {
throw new ArgumentNullException(nameof(settings)); throw new ArgumentNullException(nameof(configuration));
} }
if (masterKey == null) if (masterKey == null)
@ -32,19 +24,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
throw new ArgumentNullException(nameof(masterKey)); throw new ArgumentNullException(nameof(masterKey));
} }
Settings = settings; Configuration = configuration;
MasterKey = masterKey; MasterKey = masterKey;
_log = services.GetLogger<CngCbcAuthenticatedEncryptorDescriptor>();
} }
internal ISecret MasterKey { get; } internal ISecret MasterKey { get; }
internal CngCbcAuthenticatedEncryptionSettings Settings { get; } internal CngCbcAuthenticatedEncryptorConfiguration Configuration { get; }
public IAuthenticatedEncryptor CreateEncryptorInstance()
{
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _log);
}
public XmlSerializedDescriptorInfo ExportToXml() public XmlSerializedDescriptorInfo ExportToXml()
{ {
@ -56,18 +42,18 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// </descriptor> // </descriptor>
var encryptionElement = new XElement("encryption", var encryptionElement = new XElement("encryption",
new XAttribute("algorithm", Settings.EncryptionAlgorithm), new XAttribute("algorithm", Configuration.EncryptionAlgorithm),
new XAttribute("keyLength", Settings.EncryptionAlgorithmKeySize)); new XAttribute("keyLength", Configuration.EncryptionAlgorithmKeySize));
if (Settings.EncryptionAlgorithmProvider != null) if (Configuration.EncryptionAlgorithmProvider != null)
{ {
encryptionElement.SetAttributeValue("provider", Settings.EncryptionAlgorithmProvider); encryptionElement.SetAttributeValue("provider", Configuration.EncryptionAlgorithmProvider);
} }
var hashElement = new XElement("hash", var hashElement = new XElement("hash",
new XAttribute("algorithm", Settings.HashAlgorithm)); new XAttribute("algorithm", Configuration.HashAlgorithm));
if (Settings.HashAlgorithmProvider != null) if (Configuration.HashAlgorithmProvider != null)
{ {
hashElement.SetAttributeValue("provider", Settings.HashAlgorithmProvider); hashElement.SetAttributeValue("provider", Configuration.HashAlgorithmProvider);
} }
var rootElement = new XElement("descriptor", var rootElement = new XElement("descriptor",

View File

@ -12,18 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// </summary> /// </summary>
public sealed class CngCbcAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer public sealed class CngCbcAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
{ {
private readonly IServiceProvider _services;
public CngCbcAuthenticatedEncryptorDescriptorDeserializer()
: this(services: null)
{
}
public CngCbcAuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
{
_services = services;
}
/// <summary> /// <summary>
/// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML. /// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML.
/// </summary> /// </summary>
@ -41,20 +29,20 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// <masterKey>...</masterKey> // <masterKey>...</masterKey>
// </descriptor> // </descriptor>
var settings = new CngCbcAuthenticatedEncryptionSettings(); var configuration = new CngCbcAuthenticatedEncryptorConfiguration();
var encryptionElement = element.Element("encryption"); var encryptionElement = element.Element("encryption");
settings.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm"); configuration.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
settings.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength"); configuration.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
settings.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null configuration.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
var hashElement = element.Element("hash"); var hashElement = element.Element("hash");
settings.HashAlgorithm = (string)hashElement.Attribute("algorithm"); configuration.HashAlgorithm = (string)hashElement.Attribute("algorithm");
settings.HashAlgorithmProvider = (string)hashElement.Attribute("provider"); // could be null configuration.HashAlgorithmProvider = (string)hashElement.Attribute("provider"); // could be null
Secret masterKey = ((string)element.Element("masterKey")).ToSecret(); Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
return new CngCbcAuthenticatedEncryptorDescriptor(settings, masterKey, _services); return new CngCbcAuthenticatedEncryptorDescriptor(configuration, masterKey);
} }
} }
} }

View File

@ -1,7 +1,7 @@
// 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. // 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 namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
@ -9,36 +9,67 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// Represents a configured authenticated encryption mechanism which uses /// Represents a configured authenticated encryption mechanism which uses
/// Windows CNG algorithms in GCM encryption + authentication modes. /// Windows CNG algorithms in GCM encryption + authentication modes.
/// </summary> /// </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) /// <summary>
: this(settings, services: null) /// 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);
} }
} }
} }

View File

@ -9,22 +9,15 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
/// <summary> /// <summary>
/// A descriptor which can create an authenticated encryption system based upon the /// 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> /// </summary>
public sealed class CngGcmAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor public sealed class CngGcmAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
{ {
private readonly ILogger _log; public CngGcmAuthenticatedEncryptorDescriptor(CngGcmAuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
public CngGcmAuthenticatedEncryptorDescriptor(CngGcmAuthenticatedEncryptionSettings settings, ISecret masterKey)
: this(settings, masterKey, services: null)
{ {
} if (configuration == null)
public CngGcmAuthenticatedEncryptorDescriptor(CngGcmAuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
{
if (settings == null)
{ {
throw new ArgumentNullException(nameof(settings)); throw new ArgumentNullException(nameof(configuration));
} }
if (masterKey == null) if (masterKey == null)
@ -32,19 +25,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
throw new ArgumentNullException(nameof(masterKey)); throw new ArgumentNullException(nameof(masterKey));
} }
Settings = settings; Configuration = configuration;
MasterKey = masterKey; MasterKey = masterKey;
_log = services.GetLogger<CngGcmAuthenticatedEncryptorDescriptor>();
} }
internal ISecret MasterKey { get; } internal ISecret MasterKey { get; }
internal CngGcmAuthenticatedEncryptionSettings Settings { get; } internal CngGcmAuthenticatedEncryptorConfiguration Configuration { get; }
public IAuthenticatedEncryptor CreateEncryptorInstance()
{
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _log);
}
public XmlSerializedDescriptorInfo ExportToXml() public XmlSerializedDescriptorInfo ExportToXml()
{ {
@ -55,11 +42,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// </descriptor> // </descriptor>
var encryptionElement = new XElement("encryption", var encryptionElement = new XElement("encryption",
new XAttribute("algorithm", Settings.EncryptionAlgorithm), new XAttribute("algorithm", Configuration.EncryptionAlgorithm),
new XAttribute("keyLength", Settings.EncryptionAlgorithmKeySize)); new XAttribute("keyLength", Configuration.EncryptionAlgorithmKeySize));
if (Settings.EncryptionAlgorithmProvider != null) if (Configuration.EncryptionAlgorithmProvider != null)
{ {
encryptionElement.SetAttributeValue("provider", Settings.EncryptionAlgorithmProvider); encryptionElement.SetAttributeValue("provider", Configuration.EncryptionAlgorithmProvider);
} }
var rootElement = new XElement("descriptor", var rootElement = new XElement("descriptor",

View File

@ -12,17 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// </summary> /// </summary>
public sealed class CngGcmAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer public sealed class CngGcmAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
{ {
private readonly IServiceProvider _services;
public CngGcmAuthenticatedEncryptorDescriptorDeserializer()
: this(services: null)
{
}
public CngGcmAuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
{
_services = services;
}
/// <summary> /// <summary>
/// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML. /// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML.
@ -40,16 +29,16 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// <masterKey>...</masterKey> // <masterKey>...</masterKey>
// </descriptor> // </descriptor>
var settings = new CngGcmAuthenticatedEncryptionSettings(); var configuration = new CngGcmAuthenticatedEncryptorConfiguration();
var encryptionElement = element.Element("encryption"); var encryptionElement = element.Element("encryption");
settings.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm"); configuration.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
settings.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength"); configuration.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
settings.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null configuration.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
Secret masterKey = ((string)element.Element("masterKey")).ToSecret(); Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
return new CngGcmAuthenticatedEncryptorDescriptor(settings, masterKey, _services); return new CngGcmAuthenticatedEncryptorDescriptor(configuration, masterKey);
} }
} }
} }

View File

@ -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));
}
}
}

View File

@ -12,17 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// </summary> /// </summary>
public interface IAuthenticatedEncryptorDescriptor 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> /// <summary>
/// Exports the current descriptor to XML. /// Exports the current descriptor to XML.
/// </summary> /// </summary>

View File

@ -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. // 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 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> /// <summary>
/// A type that knows how to create instances of an <see cref="IAuthenticatedEncryptorDescriptor"/> /// A type that knows how to create instances of an <see cref="IAuthenticatedEncryptorDescriptor"/>
/// given specific secret key material. /// given specific secret key material.
/// </summary> /// </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> /// <summary>
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this /// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this configuration
/// configuration given specific secret key material. /// given specific secret key material.
/// </summary> /// </summary>
IAuthenticatedEncryptorDescriptor CreateDescriptorFromSecret(ISecret secret); IAuthenticatedEncryptorDescriptor CreateDescriptorFromSecret(ISecret secret);
/// <summary>
/// Performs a self-test of the algorithm specified by the configuration object.
/// </summary>
void Validate();
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
@ -10,36 +11,97 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// managed <see cref="System.Security.Cryptography.SymmetricAlgorithm"/> and /// managed <see cref="System.Security.Cryptography.SymmetricAlgorithm"/> and
/// <see cref="System.Security.Cryptography.KeyedHashAlgorithm"/> types. /// <see cref="System.Security.Cryptography.KeyedHashAlgorithm"/> types.
/// </summary> /// </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) /// <summary>
: this(settings, services: null) /// 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; } // Any changes to this method should also be be reflected
// in ManagedAuthenticatedEncryptorDescriptorDeserializer.FriendlyNameToType.
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor() private static string TypeToFriendlyName(Type type)
{ {
return this.CreateNewDescriptorCore(); if (type == typeof(Aes))
} {
return nameof(Aes);
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret) }
{ else if (type == typeof(HMACSHA1))
return new ManagedAuthenticatedEncryptorDescriptor(Settings, secret, _services); {
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;
}
} }
} }
} }

View File

@ -4,28 +4,20 @@
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{ {
/// <summary> /// <summary>
/// A descriptor which can create an authenticated encryption system based upon the /// 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> /// </summary>
public sealed class ManagedAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor public sealed class ManagedAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
{ {
private readonly ILogger _log; public ManagedAuthenticatedEncryptorDescriptor(ManagedAuthenticatedEncryptorConfiguration configuration, ISecret masterKey)
public ManagedAuthenticatedEncryptorDescriptor(ManagedAuthenticatedEncryptionSettings settings, ISecret masterKey)
: this(settings, masterKey, services: null)
{ {
} if (configuration == null)
public ManagedAuthenticatedEncryptorDescriptor(ManagedAuthenticatedEncryptionSettings settings, ISecret masterKey, IServiceProvider services)
{
if (settings == null)
{ {
throw new ArgumentNullException(nameof(settings)); throw new ArgumentNullException(nameof(configuration));
} }
if (masterKey == null) if (masterKey == null)
@ -33,19 +25,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
throw new ArgumentNullException(nameof(masterKey)); throw new ArgumentNullException(nameof(masterKey));
} }
Settings = settings; Configuration = configuration;
MasterKey = masterKey; MasterKey = masterKey;
_log = services.GetLogger<ManagedAuthenticatedEncryptorDescriptor>();
} }
internal ISecret MasterKey { get; } internal ISecret MasterKey { get; }
internal ManagedAuthenticatedEncryptionSettings Settings { get; } internal ManagedAuthenticatedEncryptorConfiguration Configuration { get; }
public IAuthenticatedEncryptor CreateEncryptorInstance()
{
return Settings.CreateAuthenticatedEncryptorInstance(MasterKey, _log);
}
public XmlSerializedDescriptorInfo ExportToXml() public XmlSerializedDescriptorInfo ExportToXml()
{ {
@ -57,11 +43,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// </descriptor> // </descriptor>
var encryptionElement = new XElement("encryption", var encryptionElement = new XElement("encryption",
new XAttribute("algorithm", TypeToFriendlyName(Settings.EncryptionAlgorithmType)), new XAttribute("algorithm", TypeToFriendlyName(Configuration.EncryptionAlgorithmType)),
new XAttribute("keyLength", Settings.EncryptionAlgorithmKeySize)); new XAttribute("keyLength", Configuration.EncryptionAlgorithmKeySize));
var validationElement = new XElement("validation", var validationElement = new XElement("validation",
new XAttribute("algorithm", TypeToFriendlyName(Settings.ValidationAlgorithmType))); new XAttribute("algorithm", TypeToFriendlyName(Configuration.ValidationAlgorithmType)));
var rootElement = new XElement("descriptor", var rootElement = new XElement("descriptor",
new XComment(" Algorithms provided by specified SymmetricAlgorithm and KeyedHashAlgorithm "), new XComment(" Algorithms provided by specified SymmetricAlgorithm and KeyedHashAlgorithm "),

View File

@ -13,18 +13,6 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// </summary> /// </summary>
public sealed class ManagedAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer public sealed class ManagedAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
{ {
private readonly IServiceProvider _services;
public ManagedAuthenticatedEncryptorDescriptorDeserializer()
: this(services: null)
{
}
public ManagedAuthenticatedEncryptorDescriptorDeserializer(IServiceProvider services)
{
_services = services;
}
/// <summary> /// <summary>
/// Imports the <see cref="ManagedAuthenticatedEncryptorDescriptor"/> from serialized XML. /// Imports the <see cref="ManagedAuthenticatedEncryptorDescriptor"/> from serialized XML.
/// </summary> /// </summary>
@ -42,18 +30,18 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// <masterKey>...</masterKey> // <masterKey>...</masterKey>
// </descriptor> // </descriptor>
var settings = new ManagedAuthenticatedEncryptionSettings(); var configuration = new ManagedAuthenticatedEncryptorConfiguration();
var encryptionElement = element.Element("encryption"); var encryptionElement = element.Element("encryption");
settings.EncryptionAlgorithmType = FriendlyNameToType((string)encryptionElement.Attribute("algorithm")); configuration.EncryptionAlgorithmType = FriendlyNameToType((string)encryptionElement.Attribute("algorithm"));
settings.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength"); configuration.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
var validationElement = element.Element("validation"); 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(); 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 // Any changes to this method should also be be reflected

View File

@ -1,8 +1,6 @@
// 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. // 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 namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
{ {
/// <summary> /// <summary>

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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>;
}
}
}
}

View File

@ -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>;
}
}
}
}

View File

@ -4,11 +4,16 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.DataProtection.XmlEncryption; using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Win32; using Microsoft.Win32;
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml #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)); throw new ArgumentNullException(nameof(sink));
} }
builder.Services.AddSingleton<IKeyEscrowSink>(sink); builder.Services.Configure<KeyManagementOptions>(options =>
{
options.KeyEscrowSinks.Add(sink);
});
return builder; return builder;
} }
@ -89,7 +98,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(builder)); 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; return builder;
} }
@ -114,7 +131,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(factory)); 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; return builder;
} }
@ -182,7 +207,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(directory)); 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; return builder;
} }
@ -204,7 +237,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(registryKey)); 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; return builder;
} }
@ -228,7 +269,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(certificate)); 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; return builder;
} }
@ -256,12 +305,20 @@ namespace Microsoft.AspNetCore.DataProtection
throw Error.CertificateXmlEncryptor_CertificateNotFound(thumbprint); throw Error.CertificateXmlEncryptor_CertificateNotFound(thumbprint);
} }
var services = builder.Services;
// ICertificateResolver is necessary for this type to work correctly, so register it // ICertificateResolver is necessary for this type to work correctly, so register it
// if it doesn't already exist. // if it doesn't already exist.
services.TryAdd(DataProtectionServiceDescriptors.ICertificateResolver_Default()); builder.Services.TryAddSingleton<ICertificateResolver, CertificateResolver>();
Use(services, DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(thumbprint));
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; return builder;
} }
@ -305,7 +362,16 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(builder)); 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; return builder;
} }
@ -358,7 +424,16 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(protectionDescriptorRule)); 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; return builder;
} }
@ -395,21 +470,21 @@ namespace Microsoft.AspNetCore.DataProtection
/// by default when generating protected payloads. /// by default when generating protected payloads.
/// </summary> /// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param> /// <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> /// <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) if (builder == null)
{ {
throw new ArgumentNullException(nameof(builder)); 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> /// <summary>
@ -419,25 +494,25 @@ namespace Microsoft.AspNetCore.DataProtection
/// <see cref="ValidationAlgorithm"/> enumerations. /// <see cref="ValidationAlgorithm"/> enumerations.
/// </summary> /// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param> /// <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> /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
/// <remarks> /// <remarks>
/// This API is only available on Windows. /// This API is only available on Windows.
/// </remarks> /// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)] [EditorBrowsable(EditorBrowsableState.Advanced)]
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngCbcAuthenticatedEncryptionSettings settings) public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngCbcAuthenticatedEncryptorConfiguration configuration)
{ {
if (builder == null) if (builder == null)
{ {
throw new ArgumentNullException(nameof(builder)); 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> /// <summary>
@ -447,25 +522,25 @@ namespace Microsoft.AspNetCore.DataProtection
/// <see cref="ValidationAlgorithm"/> enumerations. /// <see cref="ValidationAlgorithm"/> enumerations.
/// </summary> /// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param> /// <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> /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
/// <remarks> /// <remarks>
/// This API is only available on Windows. /// This API is only available on Windows.
/// </remarks> /// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)] [EditorBrowsable(EditorBrowsableState.Advanced)]
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngGcmAuthenticatedEncryptionSettings settings) public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngGcmAuthenticatedEncryptorConfiguration configuration)
{ {
if (builder == null) if (builder == null)
{ {
throw new ArgumentNullException(nameof(builder)); 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> /// <summary>
@ -475,28 +550,33 @@ namespace Microsoft.AspNetCore.DataProtection
/// <see cref="ValidationAlgorithm"/> enumerations. /// <see cref="ValidationAlgorithm"/> enumerations.
/// </summary> /// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param> /// <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> /// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)] [EditorBrowsable(EditorBrowsableState.Advanced)]
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, ManagedAuthenticatedEncryptionSettings settings) public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, ManagedAuthenticatedEncryptorConfiguration configuration)
{ {
if (builder == null) if (builder == null)
{ {
throw new ArgumentNullException(nameof(builder)); 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 ((IInternalAlgorithmConfiguration)configuration).Validate(); // perform self-test
Use(builder.Services, DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(settings));
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.AuthenticatedEncryptorConfiguration = configuration;
});
return builder; return builder;
} }
@ -517,30 +597,9 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(builder)); throw new ArgumentNullException(nameof(builder));
} }
Use(builder.Services, DataProtectionServiceDescriptors.IDataProtectionProvider_Ephemeral()); builder.Services.Replace(ServiceDescriptor.Singleton<IDataProtectionProvider, EphemeralDataProtectionProvider>());
return builder; 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);
}
} }
} }

View File

@ -1,79 +1,16 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.DataProtection namespace Microsoft.AspNetCore.DataProtection
{ {
/// <summary>
/// Contains static factory methods for creating <see cref="IDataProtectionProvider"/> instances.
/// </summary>
internal static class DataProtectionProviderFactory internal static class DataProtectionProviderFactory
{ {
/// <summary> public static ILoggerFactory GetDefaultLoggerFactory()
/// 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)
{ {
if (options == null) return NullLoggerFactory.Instance;
{
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;
} }
} }
} }

View File

@ -2,9 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.Internal; 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.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
@ -26,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton<IActivator, RC1ForwardingActivator>(); services.AddSingleton<IActivator, RC1ForwardingActivator>();
services.AddOptions(); services.AddOptions();
services.TryAdd(DataProtectionServices.GetDefaultServices()); AddDataProtectionServices(services);
return new DataProtectionBuilder(services); return new DataProtectionBuilder(services);
} }
@ -53,5 +59,48 @@ namespace Microsoft.Extensions.DependencyInjection
services.Configure(setupAction); services.Configure(setupAction);
return builder; 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
}
} }
} }

View File

@ -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));
}
}
}

View File

@ -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());;
}
}
}
}

View File

@ -4,6 +4,7 @@
using System; using System;
using Microsoft.AspNetCore.Cryptography.Cng; using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -22,36 +23,28 @@ namespace Microsoft.AspNetCore.DataProtection
{ {
private readonly KeyRingBasedDataProtectionProvider _dataProtectionProvider; private readonly KeyRingBasedDataProtectionProvider _dataProtectionProvider;
/// <summary>
/// Creates an ephemeral <see cref="IDataProtectionProvider"/>.
/// </summary>
public EphemeralDataProtectionProvider()
: this(services: null)
{
}
/// <summary> /// <summary>
/// Creates an ephemeral <see cref="IDataProtectionProvider"/>, optionally providing /// Creates an ephemeral <see cref="IDataProtectionProvider"/>, optionally providing
/// services (such as logging) for consumption by the provider. /// services (such as logging) for consumption by the provider.
/// </summary> /// </summary>
public EphemeralDataProtectionProvider(IServiceProvider services) public EphemeralDataProtectionProvider(ILoggerFactory loggerFactory)
{ {
IKeyRingProvider keyringProvider; IKeyRingProvider keyringProvider;
if (OSVersionUtil.IsWindows()) if (OSVersionUtil.IsWindows())
{ {
// Fastest implementation: AES-256-GCM [CNG] // Fastest implementation: AES-256-GCM [CNG]
keyringProvider = new EphemeralKeyRing<CngGcmAuthenticatedEncryptionSettings>(); keyringProvider = new EphemeralKeyRing<CngGcmAuthenticatedEncryptorConfiguration>(loggerFactory);
} }
else else
{ {
// Slowest implementation: AES-256-CBC + HMACSHA256 [Managed] // Slowest implementation: AES-256-CBC + HMACSHA256 [Managed]
keyringProvider = new EphemeralKeyRing<ManagedAuthenticatedEncryptionSettings>(); keyringProvider = new EphemeralKeyRing<ManagedAuthenticatedEncryptorConfiguration>(loggerFactory);
} }
var logger = services.GetLogger<EphemeralDataProtectionProvider>(); var logger = loggerFactory.CreateLogger<EphemeralDataProtectionProvider>();
logger?.UsingEphemeralDataProtectionProvider(); logger.UsingEphemeralDataProtectionProvider();
_dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyringProvider, services); _dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyringProvider, loggerFactory);
} }
public IDataProtector CreateProtector(string purpose) public IDataProtector CreateProtector(string purpose)
@ -66,12 +59,17 @@ namespace Microsoft.AspNetCore.DataProtection
} }
private sealed class EphemeralKeyRing<T> : IKeyRing, IKeyRingProvider 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. // Currently hardcoded to a 512-bit KDK.
private const int NUM_BYTES_IN_KDK = 512 / 8; 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); public Guid DefaultKeyId { get; } = default(Guid);
@ -85,6 +83,29 @@ namespace Microsoft.AspNetCore.DataProtection
{ {
return this; 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;
}
} }
} }
} }

View File

@ -39,13 +39,13 @@ namespace Microsoft.AspNetCore.DataProtection
public static InvalidOperationException Common_PropertyCannotBeNullOrEmpty(string propertyName) 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); return new InvalidOperationException(message);
} }
public static InvalidOperationException Common_PropertyMustBeNonNegative(string propertyName) 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); return new InvalidOperationException(message);
} }
@ -56,13 +56,13 @@ namespace Microsoft.AspNetCore.DataProtection
public static CryptographicException Common_KeyNotFound(Guid id) 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); return new CryptographicException(message);
} }
public static CryptographicException Common_KeyRevoked(Guid id) 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); return new CryptographicException(message);
} }
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.DataProtection
public static InvalidOperationException XmlKeyManager_DuplicateKey(Guid keyId) 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); return new InvalidOperationException(message);
} }
} }

View File

@ -1,9 +1,9 @@
// 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. // 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 System.IO;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.DataProtection namespace Microsoft.AspNetCore.DataProtection
{ {
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.DataProtection
/// <para> /// <para>
/// Similarly, when a developer modifies the default protected payload cryptographic /// Similarly, when a developer modifies the default protected payload cryptographic
/// algorithms, it is intended that he also select an explitiy key storage location. /// 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)"/>, /// should therefore generally be paired with a call to <see cref="DataProtectionBuilderExtensions.PersistKeysToFileSystem(IDataProtectionBuilder,DirectoryInfo)"/>,
/// for example. /// for example.
/// </para> /// </para>
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.DataProtection
/// <para> /// <para>
/// Similarly, when a developer modifies the default protected payload cryptographic /// Similarly, when a developer modifies the default protected payload cryptographic
/// algorithms, it is intended that he also select an explitiy key storage location. /// 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)"/>, /// should therefore generally be paired with a call to <see cref="DataProtectionBuilderExtensions.PersistKeysToFileSystem(IDataProtectionBuilder,DirectoryInfo)"/>,
/// for example. /// for example.
/// </para> /// </para>

View File

@ -2,18 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; 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;
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 namespace Microsoft.AspNetCore.DataProtection.Internal
{ {

View File

@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using 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();
}
}
}

View File

@ -0,0 +1,66 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.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));
}
}
}

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
@ -28,6 +29,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IEnumerable<IAuthenticatedEncryptorFactory> _encryptorFactories;
/// <summary> /// <summary>
/// The maximum skew that is allowed between servers. /// The maximum skew that is allowed between servers.
/// This is used to allow newly-created keys to be used across servers even though /// 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> /// </remarks>
private readonly TimeSpan _maxServerToServerClockSkew; private readonly TimeSpan _maxServerToServerClockSkew;
public DefaultKeyResolver(TimeSpan keyPropagationWindow, TimeSpan maxServerToServerClockSkew, IServiceProvider services) public DefaultKeyResolver(IOptions<KeyManagementOptions> keyManagementOptions, ILoggerFactory loggerFactory)
{ {
_keyPropagationWindow = keyPropagationWindow; _keyPropagationWindow = keyManagementOptions.Value.KeyPropagationWindow;
_maxServerToServerClockSkew = maxServerToServerClockSkew; _maxServerToServerClockSkew = keyManagementOptions.Value.MaxServerClockSkew;
_logger = services.GetLogger<DefaultKeyResolver>(); _encryptorFactories = keyManagementOptions.Value.AuthenticatedEncryptorFactories;
_logger = loggerFactory.CreateLogger<DefaultKeyResolver>();
} }
private bool CanCreateAuthenticatedEncryptor(IKey key) private bool CanCreateAuthenticatedEncryptor(IKey key)
{ {
try 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; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger?.KeyIsIneligibleToBeTheDefaultKeyBecauseItsMethodFailed(key.KeyId, nameof(IKey.CreateEncryptorInstance), ex); _logger.KeyIsIneligibleToBeTheDefaultKeyBecauseItsMethodFailed(key.KeyId, nameof(IAuthenticatedEncryptorFactory.CreateEncryptorInstance), ex);
return false; return false;
} }
} }
@ -70,12 +88,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (preferredDefaultKey != null) 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 the key has been revoked or is expired, it is no longer a candidate
if (preferredDefaultKey.IsRevoked || preferredDefaultKey.IsExpired(now) || !CanCreateAuthenticatedEncryptor(preferredDefaultKey)) if (preferredDefaultKey.IsRevoked || preferredDefaultKey.IsExpired(now) || !CanCreateAuthenticatedEncryptor(preferredDefaultKey))
{ {
_logger?.KeyIsNoLongerUnderConsiderationAsDefault(preferredDefaultKey.KeyId); _logger.KeyIsNoLongerUnderConsiderationAsDefault(preferredDefaultKey.KeyId);
preferredDefaultKey = null; preferredDefaultKey = null;
} }
} }
@ -98,7 +116,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (callerShouldGenerateNewKey) if (callerShouldGenerateNewKey)
{ {
_logger?.DefaultKeyExpirationImminentAndRepository(); _logger.DefaultKeyExpirationImminentAndRepository();
} }
fallbackKey = null; fallbackKey = null;
@ -119,7 +137,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
where !key.IsRevoked && CanCreateAuthenticatedEncryptor(key) where !key.IsRevoked && CanCreateAuthenticatedEncryptor(key)
select key).FirstOrDefault(); select key).FirstOrDefault();
_logger?.RepositoryContainsNoViableDefaultKey(); _logger.RepositoryContainsNoViableDefaultKey();
callerShouldGenerateNewKey = true; callerShouldGenerateNewKey = true;
return null; return null;

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.AspNetCore.DataProtection.XmlEncryption; using Microsoft.AspNetCore.DataProtection.XmlEncryption;
@ -23,11 +22,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
DateTimeOffset expirationDate, DateTimeOffset expirationDate,
IInternalXmlKeyManager keyManager, IInternalXmlKeyManager keyManager,
XElement keyElement) 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 // 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. // of time. Since it might contain sensitive information, we should protect it.
@ -35,7 +34,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
try try
{ {
return () => keyManager.DeserializeDescriptorFromKeyElement(encryptedKeyElement.ToXElement()).CreateEncryptorInstance(); return () => keyManager.DeserializeDescriptorFromKeyElement(encryptedKeyElement.ToXElement());
} }
finally finally
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
@ -45,10 +46,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
Guid KeyId { get; } Guid KeyId { get; }
/// <summary> /// <summary>
/// Creates an IAuthenticatedEncryptor instance that can be used to encrypt data /// Gets the <see cref="IAuthenticatedEncryptorDescriptor"/> instance associated with this key.
/// to and decrypt data from this key.
/// </summary> /// </summary>
/// <returns>An IAuthenticatedEncryptor.</returns> IAuthenticatedEncryptorDescriptor Descriptor { get; }
IAuthenticatedEncryptor CreateEncryptorInstance();
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
{ {
@ -14,8 +15,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
{ {
private readonly CancellationToken _expirationToken; private readonly CancellationToken _expirationToken;
internal CacheableKeyRing(CancellationToken expirationToken, DateTimeOffset expirationTime, IKey defaultKey, IEnumerable<IKey> allKeys) internal CacheableKeyRing(CancellationToken expirationToken, DateTimeOffset expirationTime, IKey defaultKey, IEnumerable<IKey> allKeys, IEnumerable<IAuthenticatedEncryptorFactory> encryptorFactories)
: this(expirationToken, expirationTime, keyRing: new KeyRing(defaultKey, allKeys)) : this(expirationToken, expirationTime, keyRing: new KeyRing(defaultKey, allKeys, encryptorFactories))
{ {
} }

View File

@ -1,7 +1,7 @@
// 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. // 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 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. /// The default key, may be null if no key is a good default candidate.
/// </summary> /// </summary>
/// <remarks> /// <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. /// so is appropriate for use with deferred keys.
/// </remarks> /// </remarks>
public IKey DefaultKey; public IKey DefaultKey;
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
/// be null if there is no viable fallback key. /// be null if there is no viable fallback key.
/// </summary> /// </summary>
/// <remarks> /// <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. /// so is appropriate for use with deferred keys.
/// </remarks> /// </remarks>
public IKey FallbackKey; public IKey FallbackKey;

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
internal sealed class Key : KeyBase internal sealed class Key : KeyBase
{ {
public Key(Guid keyId, DateTimeOffset creationDate, DateTimeOffset activationDate, DateTimeOffset expirationDate, IAuthenticatedEncryptorDescriptor descriptor) 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))
{ {
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
@ -11,15 +12,15 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
/// </summary> /// </summary>
internal abstract class KeyBase : IKey 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; KeyId = keyId;
CreationDate = creationDate; CreationDate = creationDate;
ActivationDate = activationDate; ActivationDate = activationDate;
ExpirationDate = expirationDate; ExpirationDate = expirationDate;
_lazyEncryptor = lazyEncryptor; _lazyDescriptor = lazyDescriptor;
} }
public DateTimeOffset ActivationDate { get; } public DateTimeOffset ActivationDate { get; }
@ -32,9 +33,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
public Guid KeyId { get; } public Guid KeyId { get; }
public IAuthenticatedEncryptor CreateEncryptorInstance() public IAuthenticatedEncryptorDescriptor Descriptor
{ {
return _lazyEncryptor.Value; get
{
return _lazyDescriptor.Value;
}
} }
internal void SetRevoked() internal void SetRevoked()

View File

@ -2,6 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; 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 namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
@ -24,8 +29,21 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
if (other != null) if (other != null)
{ {
this.AutoGenerateKeys = other.AutoGenerateKeys; AutoGenerateKeys = other.AutoGenerateKeys;
this._newKeyLifetime = other._newKeyLifetime; _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; _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>();
} }
} }

View File

@ -17,12 +17,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private readonly KeyHolder _defaultKeyHolder; private readonly KeyHolder _defaultKeyHolder;
private readonly Dictionary<Guid, KeyHolder> _keyIdToKeyHolderMap; 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>(); _keyIdToKeyHolderMap = new Dictionary<Guid, KeyHolder>();
foreach (IKey key in allKeys) 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', // 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. // wasn't in the underlying repository. In this case, we just add it now.
if (!_keyIdToKeyHolderMap.ContainsKey(defaultKey.KeyId)) if (!_keyIdToKeyHolderMap.ContainsKey(defaultKey.KeyId))
{ {
_keyIdToKeyHolderMap.Add(defaultKey.KeyId, new KeyHolder(defaultKey)); _keyIdToKeyHolderMap.Add(defaultKey.KeyId, new KeyHolder(defaultKey, encryptorFactories));
} }
DefaultKeyId = defaultKey.KeyId; DefaultKeyId = defaultKey.KeyId;
@ -61,17 +61,19 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
private readonly IKey _key; private readonly IKey _key;
private IAuthenticatedEncryptor _encryptor; private IAuthenticatedEncryptor _encryptor;
private readonly IEnumerable<IAuthenticatedEncryptorFactory> _encryptorFactories;
internal KeyHolder(IKey key) internal KeyHolder(IKey key, IEnumerable<IAuthenticatedEncryptorFactory> encryptorFactories)
{ {
_key = key; _key = key;
_encryptorFactories = encryptorFactories;
} }
internal IAuthenticatedEncryptor GetEncryptorInstance(out bool isRevoked) internal IAuthenticatedEncryptor GetEncryptorInstance(out bool isRevoked)
{ {
// simple double-check lock pattern // simple double-check lock pattern
// we can't use LazyInitializer<T> because we don't have a simple value factory // 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) if (encryptor == null)
{ {
lock (this) lock (this)
@ -79,7 +81,14 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
encryptor = Volatile.Read(ref _encryptor); encryptor = Volatile.Read(ref _encryptor);
if (encryptor == null) if (encryptor == null)
{ {
encryptor = _key.CreateEncryptorInstance(); foreach (var factory in _encryptorFactories)
{
encryptor = factory.CreateEncryptorInstance(_key);
if (encryptor != null)
{
break;
}
}
Volatile.Write(ref _encryptor, encryptor); Volatile.Write(ref _encryptor, encryptor);
} }
} }

View File

@ -12,10 +12,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private readonly IKeyRingProvider _keyRingProvider; private readonly IKeyRingProvider _keyRingProvider;
private readonly ILogger _logger; private readonly ILogger _logger;
public KeyRingBasedDataProtectionProvider(IKeyRingProvider keyRingProvider, IServiceProvider services) public KeyRingBasedDataProtectionProvider(IKeyRingProvider keyRingProvider, ILoggerFactory loggerFactory)
{ {
_keyRingProvider = keyRingProvider; _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) public IDataProtector CreateProtector(string purpose)

View File

@ -246,7 +246,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
var requestedEncryptor = currentKeyRing.GetAuthenticatedEncryptorByKeyId(keyIdFromPayload, out keyWasRevoked); var requestedEncryptor = currentKeyRing.GetAuthenticatedEncryptorByKeyId(keyIdFromPayload, out keyWasRevoked);
if (requestedEncryptor == null) if (requestedEncryptor == null)
{ {
_logger?.KeyWasNotFoundInTheKeyRingUnprotectOperationCannotProceed(keyIdFromPayload); _logger.KeyWasNotFoundInTheKeyRingUnprotectOperationCannotProceed(keyIdFromPayload);
throw Error.Common_KeyNotFound(keyIdFromPayload); throw Error.Common_KeyNotFound(keyIdFromPayload);
} }
@ -262,12 +262,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
if (allowOperationsOnRevokedKeys) if (allowOperationsOnRevokedKeys)
{ {
_logger?.KeyWasRevokedCallerRequestedUnprotectOperationProceedRegardless(keyIdFromPayload); _logger.KeyWasRevokedCallerRequestedUnprotectOperationProceedRegardless(keyIdFromPayload);
status = UnprotectStatus.DecryptionKeyWasRevoked; status = UnprotectStatus.DecryptionKeyWasRevoked;
} }
else else
{ {
_logger?.KeyWasRevokedUnprotectOperationCannotProceed(keyIdFromPayload); _logger.KeyWasRevokedUnprotectOperationCannotProceed(keyIdFromPayload);
throw Error.Common_KeyRevoked(keyIdFromPayload); throw Error.Common_KeyRevoked(keyIdFromPayload);
} }
} }

View File

@ -6,9 +6,10 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
@ -22,14 +23,32 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private readonly IKeyManager _keyManager; private readonly IKeyManager _keyManager;
private readonly ILogger _logger; 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; _keyManager = keyManager;
_cacheableKeyRingProvider = services?.GetService<ICacheableKeyRingProvider>() ?? this; _cacheableKeyRingProvider = cacheableKeyRingProvider ?? this;
_logger = services?.GetLogger<KeyRingProvider>(); _defaultKeyResolver = defaultKeyResolver;
_defaultKeyResolver = services?.GetService<IDefaultKeyResolver>() _logger = loggerFactory.CreateLogger<KeyRingProvider>();
?? new DefaultKeyResolver(_keyManagementOptions.KeyPropagationWindow, _keyManagementOptions.MaxServerClockSkew, services);
} }
private CacheableKeyRing CreateCacheableKeyRingCore(DateTimeOffset now, IKey keyJustAdded) private CacheableKeyRing CreateCacheableKeyRingCore(DateTimeOffset now, IKey keyJustAdded)
@ -46,7 +65,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, defaultKeyPolicy.DefaultKey, allKeys); 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 // 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 // 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; var keyToUse = defaultKeyPolicy.DefaultKey ?? defaultKeyPolicy.FallbackKey;
if (keyToUse == null) if (keyToUse == null)
{ {
_logger?.KeyRingDoesNotContainValidDefaultKey(); _logger.KeyRingDoesNotContainValidDefaultKey();
throw new InvalidOperationException(Resources.KeyRingProvider_NoDefaultKey_AutoGenerateDisabled); throw new InvalidOperationException(Resources.KeyRingProvider_NoDefaultKey_AutoGenerateDisabled);
} }
else else
{ {
_logger?.UsingFallbackKeyWithExpirationAsDefaultKey(keyToUse.KeyId, keyToUse.ExpirationDate); _logger.UsingFallbackKeyWithExpirationAsDefaultKey(keyToUse.KeyId, keyToUse.ExpirationDate);
return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, keyToUse, allKeys); return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, keyToUse, allKeys);
} }
} }
@ -99,9 +118,9 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
Debug.Assert(defaultKey != null); Debug.Assert(defaultKey != null);
// Invariant: our caller ensures that CreateEncryptorInstance succeeded at least once // 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); var nextAutoRefreshTime = now + GetRefreshPeriodWithJitter(_keyManagementOptions.KeyRingRefreshPeriod);
@ -116,7 +135,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
expirationToken: cacheExpirationToken, expirationToken: cacheExpirationToken,
expirationTime: (defaultKey.ExpirationDate <= now) ? nextAutoRefreshTime : Min(defaultKey.ExpirationDate, nextAutoRefreshTime), expirationTime: (defaultKey.ExpirationDate <= now) ? nextAutoRefreshTime : Min(defaultKey.ExpirationDate, nextAutoRefreshTime),
defaultKey: defaultKey, defaultKey: defaultKey,
allKeys: allKeys); allKeys: allKeys,
encryptorFactories: _keyManagementOptions.AuthenticatedEncryptorFactories);
} }
public IKeyRing GetCurrentKeyRing() public IKeyRing GetCurrentKeyRing()
@ -156,7 +176,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (existingCacheableKeyRing != null) if (existingCacheableKeyRing != null)
{ {
_logger?.ExistingCachedKeyRingIsExpired(); _logger.ExistingCachedKeyRingIsExpired();
} }
// It's up to us to refresh the cached keyring. // It's up to us to refresh the cached keyring.
@ -171,11 +191,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
if (existingCacheableKeyRing != null) if (existingCacheableKeyRing != null)
{ {
_logger?.ErrorOccurredWhileRefreshingKeyRing(ex); _logger.ErrorOccurredWhileRefreshingKeyRing(ex);
} }
else else
{ {
_logger?.ErrorOccurredWhileReadingKeyRing(ex); _logger.ErrorOccurredWhileReadingKeyRing(ex);
} }
// Failures that occur while refreshing the keyring are most likely transient, perhaps due to a // 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) private static TimeSpan GetRefreshPeriodWithJitter(TimeSpan refreshPeriod)
{ {
// We'll fudge the refresh period up to -20% so that multiple applications don't try to // We'll fudge the refresh period up to -20% so that multiple applications don't try to

View File

@ -4,21 +4,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.Cng;
using Microsoft.AspNetCore.DataProtection.Internal; using Microsoft.AspNetCore.DataProtection.Internal;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.AspNetCore.DataProtection.Repositories; using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.DataProtection.XmlEncryption; using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static System.FormattableString; using Microsoft.Win32;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
@ -43,9 +45,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private const string RevokeAllKeysValue = "*"; private const string RevokeAllKeysValue = "*";
private readonly IActivator _activator; private readonly IActivator _activator;
private readonly IAuthenticatedEncryptorConfiguration _authenticatedEncryptorConfiguration; private readonly AlgorithmConfiguration _authenticatedEncryptorConfiguration;
private readonly IInternalXmlKeyManager _internalKeyManager;
private readonly IKeyEscrowSink _keyEscrowSink; private readonly IKeyEscrowSink _keyEscrowSink;
private readonly IInternalXmlKeyManager _internalKeyManager;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger _logger; private readonly ILogger _logger;
private CancellationTokenSource _cacheExpirationTokenSource; private CancellationTokenSource _cacheExpirationTokenSource;
@ -53,59 +56,49 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
/// <summary> /// <summary>
/// Creates an <see cref="XmlKeyManager"/>. /// Creates an <see cref="XmlKeyManager"/>.
/// </summary> /// </summary>
/// <param name="repository">The repository where keys are stored.</param> /// <param name="keyManagementOptions">The <see cref="IOptions{KeyManagementOptions}"/> instance that provides the configuration.</param>
/// <param name="configuration">Configuration for newly-created keys.</param> /// <param name="activator">The <see cref="IActivator"/>.</param>
/// <param name="services">A provider of optional services.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public XmlKeyManager( public XmlKeyManager(IOptions<KeyManagementOptions> keyManagementOptions, IActivator activator, ILoggerFactory loggerFactory)
IXmlRepository repository,
IAuthenticatedEncryptorConfiguration configuration,
IServiceProvider services)
{ {
if (repository == null) _loggerFactory = loggerFactory;
{ _logger = _loggerFactory.CreateLogger<XmlKeyManager>();
throw new ArgumentNullException(nameof(repository));
}
if (configuration == null) KeyRepository = keyManagementOptions.Value.XmlRepository;
{ KeyEncryptor = keyManagementOptions.Value.XmlEncryptor;
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.
if (KeyRepository == null) if (KeyRepository == null)
{ {
var defaultKeyServices = services.GetService<IDefaultKeyServices>(); if (KeyEncryptor != null)
KeyEncryptor = defaultKeyServices?.GetKeyEncryptor(); // optional {
KeyRepository = defaultKeyServices?.GetKeyRepository() ?? services.GetRequiredService<IXmlRepository>(); 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 = keyManagementOptions.Value.AuthenticatedEncryptorConfiguration;
_authenticatedEncryptorConfiguration = services.GetRequiredService<IAuthenticatedEncryptorConfiguration>();
_internalKeyManager = services.GetService<IInternalXmlKeyManager>() ?? this; var escrowSinks = keyManagementOptions.Value.KeyEscrowSinks;
_keyEscrowSink = services.GetKeyEscrowSink(); // not required _keyEscrowSink = escrowSinks.Count > 0 ? new AggregateKeyEscrowSink(escrowSinks) : null;
_logger = services.GetLogger<XmlKeyManager>(); // not required _activator = activator;
TriggerAndResetCacheExpirationToken(suppressLogging: true); 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; } internal IXmlEncryptor KeyEncryptor { get; }
@ -177,7 +170,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
else else
{ {
// Skip unknown elements. // Skip unknown elements.
_logger?.UnknownElementWithNameFoundInKeyringSkipping(element.Name); _logger.UnknownElementWithNameFoundInKeyringSkipping(element.Name);
} }
} }
@ -191,11 +184,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (key != null) if (key != null)
{ {
key.SetRevoked(); key.SetRevoked();
_logger?.MarkedKeyAsRevokedInTheKeyring(revokedKeyId); _logger.MarkedKeyAsRevokedInTheKeyring(revokedKeyId);
} }
else else
{ {
_logger?.TriedToProcessRevocationOfKeyButNoSuchKeyWasFound(revokedKeyId); _logger.TriedToProcessRevocationOfKeyButNoSuchKeyWasFound(revokedKeyId);
} }
} }
} }
@ -213,7 +206,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (key.CreationDate < mostRecentMassRevocationDate) if (key.CreationDate < mostRecentMassRevocationDate)
{ {
key.SetRevoked(); 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 activationDate = (DateTimeOffset)keyElement.Element(ActivationDateElementName);
DateTimeOffset expirationDate = (DateTimeOffset)keyElement.Element(ExpirationDateElementName); DateTimeOffset expirationDate = (DateTimeOffset)keyElement.Element(ExpirationDateElementName);
_logger?.FoundKey(keyId); _logger.FoundKey(keyId);
return new DeferredKey( return new DeferredKey(
keyId: keyId, keyId: keyId,
creationDate: creationDate, creationDate: creationDate,
activationDate: activationDate, activationDate: activationDate,
expirationDate: expirationDate, expirationDate: expirationDate,
keyManager: _internalKeyManager, keyManager: this,
keyElement: keyElement); keyElement: keyElement);
} }
catch (Exception ex) 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 // this is a mass revocation of all keys as of the specified revocation date
DateTimeOffset massRevocationDate = (DateTimeOffset)revocationElement.Element(RevocationDateElementName); DateTimeOffset massRevocationDate = (DateTimeOffset)revocationElement.Element(RevocationDateElementName);
_logger?.FoundRevocationOfAllKeysCreatedPriorTo(massRevocationDate); _logger.FoundRevocationOfAllKeysCreatedPriorTo(massRevocationDate);
return massRevocationDate; return massRevocationDate;
} }
else else
{ {
// only one key is being revoked // only one key is being revoked
var keyId = XmlConvert.ToGuid(keyIdAsString); var keyId = XmlConvert.ToGuid(keyIdAsString);
_logger?.FoundRevocationOfKey(keyId); _logger.FoundRevocationOfKey(keyId);
return 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 // Any exceptions that occur are fatal - we don't want to continue if we cannot process
// revocation information. // revocation information.
_logger?.ExceptionWhileProcessingRevocationElement(revocationElement, ex); _logger.ExceptionWhileProcessingRevocationElement(revocationElement, ex);
throw; throw;
} }
} }
@ -299,7 +292,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// <reason>...</reason> // <reason>...</reason>
// </revocation> // </revocation>
_logger?.RevokingAllKeysAsOfForReason(revocationDate, reason); _logger.RevokingAllKeysAsOfForReason(revocationDate, reason);
var revocationElement = new XElement(RevocationElementName, var revocationElement = new XElement(RevocationElementName,
new XAttribute(VersionAttributeName, 1), new XAttribute(VersionAttributeName, 1),
@ -327,7 +320,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{ {
if (!suppressLogging) if (!suppressLogging)
{ {
_logger?.KeyCacheExpirationTokenTriggeredByOperation(opName); _logger.KeyCacheExpirationTokenTriggeredByOperation(opName);
} }
Interlocked.Exchange(ref _cacheExpirationTokenSource, new CancellationTokenSource())?.Cancel(); Interlocked.Exchange(ref _cacheExpirationTokenSource, new CancellationTokenSource())?.Cancel();
@ -341,10 +334,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// include sensitive information in the exception message. // include sensitive information in the exception message.
// write sanitized <key> element // write sanitized <key> element
_logger?.ExceptionWhileProcessingKeyElement(keyElement.WithoutChildNodes(), error); _logger.ExceptionWhileProcessingKeyElement(keyElement.WithoutChildNodes(), error);
// write full <key> element // write full <key> element
_logger?.AnExceptionOccurredWhileProcessingElementDebug(keyElement, error); _logger.AnExceptionOccurredWhileProcessingElementDebug(keyElement, error);
} }
@ -359,13 +352,13 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// </descriptor> // </descriptor>
// </key> // </key>
_logger?.CreatingKey(keyId, creationDate, activationDate, expirationDate); _logger.CreatingKey(keyId, creationDate, activationDate, expirationDate);
var newDescriptor = _authenticatedEncryptorConfiguration.CreateNewDescriptor() var newDescriptor = _authenticatedEncryptorConfiguration.CreateNewDescriptor()
?? CryptoUtil.Fail<IAuthenticatedEncryptorDescriptor>("CreateNewDescriptor returned null."); ?? CryptoUtil.Fail<IAuthenticatedEncryptorDescriptor>("CreateNewDescriptor returned null.");
var descriptorXmlInfo = newDescriptor.ExportToXml(); var descriptorXmlInfo = newDescriptor.ExportToXml();
_logger?.DescriptorDeserializerTypeForKeyIs(keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName); _logger.DescriptorDeserializerTypeForKeyIs(keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName);
// build the <key> element // build the <key> element
var keyElement = new XElement(KeyElementName, 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 key escrow policy is in effect, write the *unencrypted* key now.
if (_keyEscrowSink != null) if (_keyEscrowSink != null)
{ {
_logger?.KeyEscrowSinkFoundWritingKeyToEscrow(keyId); _logger.KeyEscrowSinkFoundWritingKeyToEscrow(keyId);
} }
else else
{ {
_logger?.NoKeyEscrowSinkFoundNotWritingKeyToEscrow(keyId); _logger.NoKeyEscrowSinkFoundNotWritingKeyToEscrow(keyId);
} }
_keyEscrowSink?.Store(keyId, keyElement); _keyEscrowSink?.Store(keyId, keyElement);
// If an XML encryptor has been configured, protect secret key material now. // If an XML encryptor has been configured, protect secret key material now.
if (KeyEncryptor == null) if (KeyEncryptor == null)
{ {
_logger?.NoXMLEncryptorConfiguredKeyMayBePersistedToStorageInUnencryptedForm(keyId); _logger.NoXMLEncryptorConfiguredKeyMayBePersistedToStorageInUnencryptedForm(keyId);
} }
var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement; var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement;
// Persist it to the underlying repository and trigger the cancellation token. // 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); KeyRepository.StoreElement(possiblyEncryptedKeyElement, friendlyName);
TriggerAndResetCacheExpirationToken(); TriggerAndResetCacheExpirationToken();
@ -440,7 +433,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// <reason>...</reason> // <reason>...</reason>
// </revocation> // </revocation>
_logger?.RevokingKeyForReason(keyId, revocationDate, reason); _logger.RevokingKeyForReason(keyId, revocationDate, reason);
var revocationElement = new XElement(RevocationElementName, var revocationElement = new XElement(RevocationElementName,
new XAttribute(VersionAttributeName, 1), new XAttribute(VersionAttributeName, 1),
@ -450,9 +443,97 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
new XElement(ReasonElementName, reason)); new XElement(ReasonElementName, reason));
// Persist it to the underlying repository and trigger the cancellation token // 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); KeyRepository.StoreElement(revocationElement, friendlyName);
TriggerAndResetCacheExpirationToken(); 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);
}
}
}
} }
} }

View File

@ -394,6 +394,22 @@ namespace Microsoft.AspNetCore.DataProtection
return string.Format(CultureInfo.CurrentCulture, GetString("LifetimeMustNotBeNegative"), p0); 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) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -12,13 +12,13 @@ namespace Microsoft.AspNetCore.DataProtection
private const string To = "Microsoft.AspNetCore.DataProtection"; private const string To = "Microsoft.AspNetCore.DataProtection";
private readonly ILogger _logger; 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) 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) public override object CreateInstance(Type expectedBaseType, string implementationTypeName)
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.DataProtection
var type = Type.GetType(forwardedImplementationTypeName, false); var type = Type.GetType(forwardedImplementationTypeName, false);
if (type != null) if (type != null)
{ {
_logger?.LogDebug("Forwarded activator type request from {FromType} to {ToType}", _logger.LogDebug("Forwarded activator type request from {FromType} to {ToType}",
implementationTypeName, implementationTypeName,
forwardedImplementationTypeName); forwardedImplementationTypeName);

View File

@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
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; }
}
}

View File

@ -7,9 +7,10 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Cryptography; 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.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging;
using Microsoft.Win32; using Microsoft.Win32;
namespace Microsoft.AspNetCore.DataProtection namespace Microsoft.AspNetCore.DataProtection
@ -19,11 +20,22 @@ namespace Microsoft.AspNetCore.DataProtection
/// </summary> /// </summary>
internal sealed class RegistryPolicyResolver 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 // 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) 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; ...". // The format of this key is "type1; type2; ...".
// We call Type.GetType to perform an eager check that the type exists. // 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) if (sinksFromRegistry != null)
{ {
foreach (string sinkFromRegistry in sinksFromRegistry.Split(';')) foreach (string sinkFromRegistry in sinksFromRegistry.Split(';'))
@ -81,69 +93,60 @@ namespace Microsoft.AspNetCore.DataProtection
} }
/// <summary> /// <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> /// </summary>
public static ServiceDescriptor[] ResolveDefaultPolicy() public static RegistryPolicy ResolveDefaultPolicy(IActivator activator, ILoggerFactory loggerFactory)
{ {
var subKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection"); return new RegistryPolicyResolver(activator, loggerFactory).ResolvePolicy();
if (subKey != null) }
internal RegistryPolicy ResolvePolicy()
{
using (var registryKey = _getPolicyRegKey())
{ {
using (subKey) return ResolvePolicyCore(registryKey); // fully evaluate enumeration while the reg key is open
{
return new RegistryPolicyResolver(subKey).ResolvePolicy();
}
}
else
{
return new ServiceDescriptor[0];
} }
} }
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 // Read the encryption options type: CNG-CBC, CNG-GCM, Managed
IInternalAuthenticatedEncryptionSettings options = null; AlgorithmConfiguration configuration = null;
string encryptionType = (string)_policyRegKey.GetValue("EncryptionType");
var encryptionType = (string)policyRegKey.GetValue("EncryptionType");
if (String.Equals(encryptionType, "CNG-CBC", StringComparison.OrdinalIgnoreCase)) if (String.Equals(encryptionType, "CNG-CBC", StringComparison.OrdinalIgnoreCase))
{ {
options = new CngCbcAuthenticatedEncryptionSettings(); configuration = new CngCbcAuthenticatedEncryptorConfiguration();
} }
else if (String.Equals(encryptionType, "CNG-GCM", StringComparison.OrdinalIgnoreCase)) else if (String.Equals(encryptionType, "CNG-GCM", StringComparison.OrdinalIgnoreCase))
{ {
options = new CngGcmAuthenticatedEncryptionSettings(); configuration = new CngGcmAuthenticatedEncryptorConfiguration();
} }
else if (String.Equals(encryptionType, "Managed", StringComparison.OrdinalIgnoreCase)) else if (String.Equals(encryptionType, "Managed", StringComparison.OrdinalIgnoreCase))
{ {
options = new ManagedAuthenticatedEncryptionSettings(); configuration = new ManagedAuthenticatedEncryptorConfiguration();
} }
else if (!String.IsNullOrEmpty(encryptionType)) else if (!String.IsNullOrEmpty(encryptionType))
{ {
throw CryptoUtil.Fail("Unrecognized EncryptionType: " + encryptionType); throw CryptoUtil.Fail("Unrecognized EncryptionType: " + encryptionType);
} }
if (options != null) if (configuration != null)
{ {
PopulateOptions(options, _policyRegKey); PopulateOptions(configuration, policyRegKey);
yield return DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(options);
} }
// Read ancillary data // Read ancillary data
int? defaultKeyLifetime = (int?)_policyRegKey.GetValue("DefaultKeyLifetime"); var defaultKeyLifetime = (int?)policyRegKey.GetValue("DefaultKeyLifetime");
if (defaultKeyLifetime.HasValue)
{
yield return DataProtectionServiceDescriptors.ConfigureOptions_DefaultKeyLifetime(defaultKeyLifetime.Value);
}
var keyEscrowSinks = ReadKeyEscrowSinks(_policyRegKey); var keyEscrowSinks = ReadKeyEscrowSinks(policyRegKey).Select(item => _activator.CreateInstance<IKeyEscrowSink>(item));
foreach (var keyEscrowSink in keyEscrowSinks)
{ return new RegistryPolicy(configuration, keyEscrowSinks, defaultKeyLifetime);
yield return DataProtectionServiceDescriptors.IKeyEscrowSink_FromTypeName(keyEscrowSink);
}
} }
} }
} }

View File

@ -17,10 +17,10 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
{ {
private readonly List<XElement> _storedElements = new List<XElement>(); private readonly List<XElement> _storedElements = new List<XElement>();
public EphemeralXmlRepository(IServiceProvider services) public EphemeralXmlRepository(ILoggerFactory loggerFactory)
{ {
var logger = services?.GetLogger<EphemeralXmlRepository>(); var logger = loggerFactory.CreateLogger<EphemeralXmlRepository>();
logger?.UsingInmemoryRepository(); logger.UsingInmemoryRepository();
} }
public virtual IReadOnlyCollection<XElement> GetAllElements() public virtual IReadOnlyCollection<XElement> GetAllElements()

View File

@ -24,21 +24,8 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
/// Creates a <see cref="FileSystemXmlRepository"/> with keys stored at the given directory. /// Creates a <see cref="FileSystemXmlRepository"/> with keys stored at the given directory.
/// </summary> /// </summary>
/// <param name="directory">The directory in which to persist key material.</param> /// <param name="directory">The directory in which to persist key material.</param>
public FileSystemXmlRepository(DirectoryInfo directory) /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
: this(directory, services: null) public FileSystemXmlRepository(DirectoryInfo directory, ILoggerFactory loggerFactory)
{
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)
{ {
if (directory == null) if (directory == null)
{ {
@ -46,8 +33,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
} }
Directory = directory; Directory = directory;
Services = services; _logger = loggerFactory.CreateLogger<FileSystemXmlRepository>();
_logger = services?.GetLogger<FileSystemXmlRepository>();
} }
/// <summary> /// <summary>
@ -65,11 +51,6 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
/// </summary> /// </summary>
public DirectoryInfo Directory { get; } 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 const string DataProtectionKeysFolderName = "DataProtection-Keys";
private static DirectoryInfo GetKeyStorageDirectoryFromBaseAppDataPath(string basePath) private static DirectoryInfo GetKeyStorageDirectoryFromBaseAppDataPath(string basePath)
@ -185,7 +166,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
private XElement ReadElementFromFile(string fullPath) private XElement ReadElementFromFile(string fullPath)
{ {
_logger?.ReadingDataFromFile(fullPath); _logger.ReadingDataFromFile(fullPath);
using (var fileStream = File.OpenRead(fullPath)) using (var fileStream = File.OpenRead(fullPath))
{ {
@ -203,7 +184,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
if (!IsSafeFilename(friendlyName)) if (!IsSafeFilename(friendlyName))
{ {
var newFriendlyName = Guid.NewGuid().ToString(); var newFriendlyName = Guid.NewGuid().ToString();
_logger?.NameIsNotSafeFileName(friendlyName, newFriendlyName); _logger.NameIsNotSafeFileName(friendlyName, newFriendlyName);
friendlyName = newFriendlyName; friendlyName = newFriendlyName;
} }
@ -229,7 +210,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
// Once the file has been fully written, perform the rename. // Once the file has been fully written, perform the rename.
// Renames are atomic operations on the file systems we support. // Renames are atomic operations on the file systems we support.
_logger?.WritingDataToFile(finalFilename); _logger.WritingDataToFile(finalFilename);
File.Move(tempFilename, finalFilename); File.Move(tempFilename, finalFilename);
} }
finally finally

View File

@ -3,14 +3,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Security.Principal; using System.Security.Principal;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Win32; using Microsoft.Win32;
using static System.FormattableString;
namespace Microsoft.AspNetCore.DataProtection.Repositories namespace Microsoft.AspNetCore.DataProtection.Repositories
{ {
/// <summary> /// <summary>
@ -26,21 +25,8 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
/// Creates a <see cref="RegistryXmlRepository"/> with keys stored in the given registry key. /// Creates a <see cref="RegistryXmlRepository"/> with keys stored in the given registry key.
/// </summary> /// </summary>
/// <param name="registryKey">The registry key in which to persist key material.</param> /// <param name="registryKey">The registry key in which to persist key material.</param>
public RegistryXmlRepository(RegistryKey registryKey) /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
: this(registryKey, services: null) public RegistryXmlRepository(RegistryKey registryKey, ILoggerFactory loggerFactory)
{
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)
{ {
if (registryKey == null) if (registryKey == null)
{ {
@ -48,8 +34,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
} }
RegistryKey = registryKey; RegistryKey = registryKey;
Services = services; _logger = loggerFactory.CreateLogger<RegistryXmlRepository>();
_logger = services?.GetLogger<RegistryXmlRepository>();
} }
/// <summary> /// <summary>
@ -67,11 +52,6 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
/// </summary> /// </summary>
public RegistryKey RegistryKey { get; } public RegistryKey RegistryKey { get; }
/// <summary>
/// The <see cref="IServiceProvider"/> provided to the constructor.
/// </summary>
protected IServiceProvider Services { get; }
public virtual IReadOnlyCollection<XElement> GetAllElements() public virtual IReadOnlyCollection<XElement> GetAllElements()
{ {
// forces complete enumeration // 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. // 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. // See APP_POOL::EnsureSharedMachineKeyStorage in WAS source for more info.
// The version number will need to change if IIS hosts Core CLR directly. // 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); var aspnetBaseKey = hklmBaseKey.OpenSubKey(aspnetAutoGenKeysBaseKeyName, writable: true);
if (aspnetBaseKey != null) if (aspnetBaseKey != null)
{ {
@ -141,7 +125,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
private XElement ReadElementFromRegKey(RegistryKey regKey, string valueName) private XElement ReadElementFromRegKey(RegistryKey regKey, string valueName)
{ {
_logger?.ReadingDataFromRegistryKeyValue(regKey, valueName); _logger.ReadingDataFromRegistryKeyValue(regKey, valueName);
var data = regKey.GetValue(valueName) as string; var data = regKey.GetValue(valueName) as string;
return (!String.IsNullOrEmpty(data)) ? XElement.Parse(data) : null; return (!String.IsNullOrEmpty(data)) ? XElement.Parse(data) : null;
@ -157,7 +141,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
if (!IsSafeRegistryValueName(friendlyName)) if (!IsSafeRegistryValueName(friendlyName))
{ {
var newFriendlyName = Guid.NewGuid().ToString(); var newFriendlyName = Guid.NewGuid().ToString();
_logger?.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName); _logger.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
friendlyName = newFriendlyName; friendlyName = newFriendlyName;
} }

View File

@ -189,4 +189,7 @@
<data name="LifetimeMustNotBeNegative" xml:space="preserve"> <data name="LifetimeMustNotBeNegative" xml:space="preserve">
<value>{0} must not be negative</value> <value>{0} must not be negative</value>
</data> </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> </root>

View File

@ -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

View File

@ -9,7 +9,6 @@ using System.Security.Cryptography.Xml;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.DataProtection.XmlEncryption namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
@ -23,29 +22,13 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
private readonly IInternalCertificateXmlEncryptor _encryptor; private readonly IInternalCertificateXmlEncryptor _encryptor;
private readonly ILogger _logger; 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> /// <summary>
/// Creates a <see cref="CertificateXmlEncryptor"/> given a certificate's thumbprint, an /// Creates a <see cref="CertificateXmlEncryptor"/> given a certificate's thumbprint, an
/// <see cref="ICertificateResolver"/> that can be used to resolve the certificate, and /// <see cref="ICertificateResolver"/> that can be used to resolve the certificate, and
/// an <see cref="IServiceProvider"/>. /// an <see cref="IServiceProvider"/>.
/// </summary> /// </summary>
/// <param name="thumbprint">The thumbprint (as a hex string) of the certificate with which to public CertificateXmlEncryptor(string thumbprint, ICertificateResolver certificateResolver, ILoggerFactory loggerFactory)
/// encrypt the key material. The certificate must be locatable by <paramref name="certificateResolver"/>.</param> : this(loggerFactory, encryptor: null)
/// <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)
{ {
if (thumbprint == null) if (thumbprint == null)
{ {
@ -60,23 +43,12 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
_certFactory = CreateCertFactory(thumbprint, certificateResolver); _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> /// <summary>
/// Creates a <see cref="CertificateXmlEncryptor"/> given an <see cref="X509Certificate2"/> instance /// Creates a <see cref="CertificateXmlEncryptor"/> given an <see cref="X509Certificate2"/> instance
/// and an <see cref="IServiceProvider"/>. /// and an <see cref="IServiceProvider"/>.
/// </summary> /// </summary>
/// <param name="certificate">The <see cref="X509Certificate2"/> with which to encrypt the key material.</param> public CertificateXmlEncryptor(X509Certificate2 certificate, ILoggerFactory loggerFactory)
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param> : this(loggerFactory, encryptor: null)
public CertificateXmlEncryptor(X509Certificate2 certificate, IServiceProvider services)
: this(services)
{ {
if (certificate == null) if (certificate == null)
{ {
@ -86,10 +58,10 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
_certFactory = () => certificate; _certFactory = () => certificate;
} }
internal CertificateXmlEncryptor(IServiceProvider services) internal CertificateXmlEncryptor(ILoggerFactory loggerFactory, IInternalCertificateXmlEncryptor encryptor)
{ {
_encryptor = services?.GetService<IInternalCertificateXmlEncryptor>() ?? this; _encryptor = encryptor ?? this;
_logger = services.GetLogger<CertificateXmlEncryptor>(); _logger = loggerFactory.CreateLogger<CertificateXmlEncryptor>();
} }
/// <summary> /// <summary>
@ -149,7 +121,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger?.ExceptionWhileTryingToResolveCertificateWithThumbprint(thumbprint, ex); _logger.ExceptionWhileTryingToResolveCertificateWithThumbprint(thumbprint, ex);
throw; throw;
} }
@ -161,7 +133,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
var cert = _certFactory() var cert = _certFactory()
?? CryptoUtil.Fail<X509Certificate2>("Cert factory returned null."); ?? CryptoUtil.Fail<X509Certificate2>("Cert factory returned null.");
_logger?.EncryptingToX509CertificateWithThumbprint(cert.Thumbprint); _logger.EncryptingToX509CertificateWithThumbprint(cert.Thumbprint);
try try
{ {
@ -169,7 +141,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger?.AnErrorOccurredWhileEncryptingToX509CertificateWithThumbprint(cert.Thumbprint, ex); _logger.AnErrorOccurredWhileEncryptingToX509CertificateWithThumbprint(cert.Thumbprint, ex);
throw; throw;
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Globalization;
using System.Security.Principal; using System.Security.Principal;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
@ -9,8 +10,6 @@ using Microsoft.AspNetCore.Cryptography.SafeHandles;
using Microsoft.AspNetCore.DataProtection.Cng; using Microsoft.AspNetCore.DataProtection.Cng;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static System.FormattableString;
namespace Microsoft.AspNetCore.DataProtection.XmlEncryption namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
{ {
/// <summary> /// <summary>
@ -29,18 +28,8 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
/// </summary> /// </summary>
/// <param name="protectionDescriptorRule">The rule string from which to create the protection descriptor.</param> /// <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="flags">Flags controlling the creation of the protection descriptor.</param>
public DpapiNGXmlEncryptor(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags) /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
: this(protectionDescriptorRule, flags, services: null) public DpapiNGXmlEncryptor(string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags, ILoggerFactory loggerFactory)
{
}
/// <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)
{ {
if (protectionDescriptorRule == null) if (protectionDescriptorRule == null)
{ {
@ -53,7 +42,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus); UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
CryptoUtil.AssertSafeHandleIsValid(_protectionDescriptorHandle); CryptoUtil.AssertSafeHandleIsValid(_protectionDescriptorHandle);
_logger = services.GetLogger<DpapiNGXmlEncryptor>(); _logger = loggerFactory.CreateLogger<DpapiNGXmlEncryptor>();
} }
/// <summary> /// <summary>
@ -73,7 +62,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
} }
var protectionDescriptorRuleString = _protectionDescriptorHandle.GetProtectionDescriptorRuleString(); 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 // Convert the XML element to a binary secret so that it can be run through DPAPI
byte[] cngDpapiEncryptedData; byte[] cngDpapiEncryptedData;
@ -86,7 +75,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger?.ErrorOccurredWhileEncryptingToWindowsDPAPING(ex); _logger.ErrorOccurredWhileEncryptingToWindowsDPAPING(ex);
throw; throw;
} }
@ -118,7 +107,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
using (var currentIdentity = WindowsIdentity.GetCurrent()) using (var currentIdentity = WindowsIdentity.GetCurrent())
{ {
// use the SID to create an SDDL string // use the SID to create an SDDL string
return Invariant($"SID={currentIdentity.User.Value}"); return string.Format(CultureInfo.InvariantCulture, "SID={0}", currentIdentity.User.Value);
} }
} }
} }

View File

@ -21,28 +21,18 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly bool _protectToLocalMachine; 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> /// <summary>
/// Creates a <see cref="DpapiXmlEncryptor"/> given a protection scope and an <see cref="IServiceProvider"/>. /// Creates a <see cref="DpapiXmlEncryptor"/> given a protection scope and an <see cref="IServiceProvider"/>.
/// </summary> /// </summary>
/// <param name="protectToLocalMachine">'true' if the data should be decipherable by anybody on the local machine, /// <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> /// '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> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public DpapiXmlEncryptor(bool protectToLocalMachine, IServiceProvider services) public DpapiXmlEncryptor(bool protectToLocalMachine, ILoggerFactory loggerFactory)
{ {
CryptoUtil.AssertPlatformIsWindows(); CryptoUtil.AssertPlatformIsWindows();
_protectToLocalMachine = protectToLocalMachine; _protectToLocalMachine = protectToLocalMachine;
_logger = services.GetLogger<DpapiXmlEncryptor>(); _logger = loggerFactory.CreateLogger<DpapiXmlEncryptor>();
} }
/// <summary> /// <summary>
@ -62,11 +52,11 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
} }
if (_protectToLocalMachine) if (_protectToLocalMachine)
{ {
_logger?.EncryptingToWindowsDPAPIForLocalMachineAccount(); _logger.EncryptingToWindowsDPAPIForLocalMachineAccount();
} }
else 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 // 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) catch (Exception ex)
{ {
_logger?.ErrorOccurredWhileEncryptingToWindowsDPAPI(ex); _logger.ErrorOccurredWhileEncryptingToWindowsDPAPI(ex);
throw; throw;
} }

View File

@ -21,4 +21,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" /> <PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>

View File

@ -21,4 +21,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" /> <PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>

View File

@ -21,4 +21,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" /> <PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>

View File

@ -20,4 +20,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" /> <PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>

View File

@ -21,4 +21,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" /> <PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>

View File

@ -5,6 +5,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.Security.Cryptography; using System.Security.Cryptography;
using Microsoft.AspNetCore.DataProtection.Extensions; using Microsoft.AspNetCore.DataProtection.Extensions;
using Microsoft.Extensions.Logging.Abstractions;
using Moq; using Moq;
using Xunit; using Xunit;
@ -152,7 +153,7 @@ namespace Microsoft.AspNetCore.DataProtection
public void RoundTrip_ProtectedData() public void RoundTrip_ProtectedData()
{ {
// Arrange // Arrange
var ephemeralProtector = new EphemeralDataProtectionProvider().CreateProtector("my purpose"); var ephemeralProtector = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("my purpose");
var timeLimitedProtector = new TimeLimitedDataProtector(ephemeralProtector); var timeLimitedProtector = new TimeLimitedDataProtector(ephemeralProtector);
var expectedExpiration = StringToDateTime("2020-01-01 00:00:00Z"); var expectedExpiration = StringToDateTime("2020-01-01 00:00:00Z");

View File

@ -21,4 +21,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" /> <PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,52 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using 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);
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using 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);
}
}
}

View File

@ -3,6 +3,8 @@
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -13,13 +15,14 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_Cbc_CreatesAppropriateDescriptor() public void ImportFromXml_Cbc_CreatesAppropriateDescriptor()
{ {
// Arrange // Arrange
var control = new AuthenticatedEncryptorDescriptor( var descriptor = new AuthenticatedEncryptorDescriptor(
new AuthenticatedEncryptionSettings() new AuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = EncryptionAlgorithm.AES_192_CBC, EncryptionAlgorithm = EncryptionAlgorithm.AES_192_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA512 ValidationAlgorithm = ValidationAlgorithm.HMACSHA512
}, },
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance(); "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
const string xml = @" const string xml = @"
<encryptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'> <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' /> <validation algorithm='HMACSHA512' />
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey> <masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
</encryptor>"; </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 // Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; 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)); byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext); 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);
}
} }
} }

View File

@ -8,9 +8,11 @@ using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Cryptography.Cng; using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.SafeHandles; using Microsoft.AspNetCore.Cryptography.SafeHandles;
using Microsoft.AspNetCore.DataProtection.Cng; using Microsoft.AspNetCore.DataProtection.Cng;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Managed; using Microsoft.AspNetCore.DataProtection.Managed;
using Microsoft.AspNetCore.DataProtection.Test.Shared; using Microsoft.AspNetCore.DataProtection.Test.Shared;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -38,7 +40,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC, symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8), symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8),
hmacAlgorithmHandle: BCryptAlgorithmHandle.OpenAlgorithmHandle(hashAlgorithm, hmac: true)); 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 // Act & assert - data round trips properly from control to test
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -64,7 +66,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
keyDerivationKey: masterKey, keyDerivationKey: masterKey,
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_GCM,
symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8)); 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 // Act & assert - data round trips properly from control to test
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -102,7 +104,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
symmetricAlgorithmFactory: () => Aes.Create(), symmetricAlgorithmFactory: () => Aes.Create(),
symmetricAlgorithmKeySizeInBytes: keyLengthInBits / 8, symmetricAlgorithmKeySizeInBytes: keyLengthInBits / 8,
validationAlgorithmFactory: validationAlgorithmFactory); 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 // Act & assert - data round trips properly from control to test
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; 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) private static AuthenticatedEncryptorDescriptor CreateDescriptor(EncryptionAlgorithm encryptionAlgorithm, ValidationAlgorithm validationAlgorithm, ISecret masterKey)
{ {
return new AuthenticatedEncryptorDescriptor(new AuthenticatedEncryptionSettings() return new AuthenticatedEncryptorDescriptor(new AuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = encryptionAlgorithm, EncryptionAlgorithm = encryptionAlgorithm,
ValidationAlgorithm = validationAlgorithm ValidationAlgorithm = validationAlgorithm
}, masterKey); }, 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);
}
} }
} }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey() public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey()
{ {
// Arrange // Arrange
var configuration = new CngCbcAuthenticatedEncryptorConfiguration(new CngCbcAuthenticatedEncryptionSettings()); var configuration = new CngCbcAuthenticatedEncryptorConfiguration();
// Act // Act
var masterKey1 = ((CngCbcAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey; var masterKey1 = ((CngCbcAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey;
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void CreateNewDescriptor_PropagatesOptions() public void CreateNewDescriptor_PropagatesOptions()
{ {
// Arrange // Arrange
var configuration = new CngCbcAuthenticatedEncryptorConfiguration(new CngCbcAuthenticatedEncryptionSettings()); var configuration = new CngCbcAuthenticatedEncryptorConfiguration();
// Act // Act
var descriptor = (CngCbcAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor(); var descriptor = (CngCbcAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor();
// Assert // Assert
Assert.Equal(configuration.Settings, descriptor.Settings); Assert.Equal(configuration, descriptor.Configuration);
} }
} }
} }

View File

@ -4,8 +4,10 @@
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Test.Shared; using Microsoft.AspNetCore.DataProtection.Test.Shared;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -17,8 +19,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_CreatesAppropriateDescriptor() public void ImportFromXml_CreatesAppropriateDescriptor()
{ {
// Arrange // Arrange
var control = new CngCbcAuthenticatedEncryptorDescriptor( var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(
new CngCbcAuthenticatedEncryptionSettings() new CngCbcAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM, EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM,
EncryptionAlgorithmKeySize = 192, EncryptionAlgorithmKeySize = 192,
@ -26,7 +28,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
HashAlgorithm = Constants.BCRYPT_SHA512_ALGORITHM, HashAlgorithm = Constants.BCRYPT_SHA512_ALGORITHM,
HashAlgorithmProvider = null HashAlgorithmProvider = null
}, },
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance(); "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
const string xml = @" const string xml = @"
<descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'> <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' /> <hash algorithm='SHA512' />
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey> <masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
</descriptor>"; </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 // Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; 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)); byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext); 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);
}
} }
} }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
// Arrange // Arrange
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret(); var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptionSettings() var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = "enc-alg", EncryptionAlgorithm = "enc-alg",
EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmKeySize = 2048,
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
// Arrange // Arrange
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret(); var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptionSettings() var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(new CngCbcAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = "enc-alg", EncryptionAlgorithm = "enc-alg",
EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmKeySize = 2048,

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey() public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey()
{ {
// Arrange // Arrange
var configuration = new CngGcmAuthenticatedEncryptorConfiguration(new CngGcmAuthenticatedEncryptionSettings()); var configuration = new CngGcmAuthenticatedEncryptorConfiguration();
// Act // Act
var masterKey1 = ((CngGcmAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey; var masterKey1 = ((CngGcmAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey;
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void CreateNewDescriptor_PropagatesOptions() public void CreateNewDescriptor_PropagatesOptions()
{ {
// Arrange // Arrange
var configuration = new CngGcmAuthenticatedEncryptorConfiguration(new CngGcmAuthenticatedEncryptionSettings()); var configuration = new CngGcmAuthenticatedEncryptorConfiguration();
// Act // Act
var descriptor = (CngGcmAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor(); var descriptor = (CngGcmAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor();
// Assert // Assert
Assert.Equal(configuration.Settings, descriptor.Settings); Assert.Equal(configuration, descriptor.Configuration);
} }
} }
} }

View File

@ -4,8 +4,10 @@
using System; using System;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Test.Shared; using Microsoft.AspNetCore.DataProtection.Test.Shared;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -17,21 +19,23 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_CreatesAppropriateDescriptor() public void ImportFromXml_CreatesAppropriateDescriptor()
{ {
// Arrange // Arrange
var control = new CngGcmAuthenticatedEncryptorDescriptor( var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(
new CngGcmAuthenticatedEncryptionSettings() new CngGcmAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM, EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM,
EncryptionAlgorithmKeySize = 192, EncryptionAlgorithmKeySize = 192,
EncryptionAlgorithmProvider = null EncryptionAlgorithmProvider = null
}, },
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance(); "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
const string xml = @" const string xml = @"
<descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'> <descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
<encryption algorithm='AES' keyLength='192' /> <encryption algorithm='AES' keyLength='192' />
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey> <masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
</descriptor>"; </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 // Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; 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)); byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext); 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);
}
} }
} }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
// Arrange // Arrange
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret(); var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptionSettings() var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = "enc-alg", EncryptionAlgorithm = "enc-alg",
EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmKeySize = 2048,
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
// Arrange // Arrange
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret(); var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptionSettings() var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(new CngGcmAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithm = "enc-alg", EncryptionAlgorithm = "enc-alg",
EncryptionAlgorithmKeySize = 2048 EncryptionAlgorithmKeySize = 2048

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey() public void CreateNewDescriptor_CreatesUniqueCorrectlySizedMasterKey()
{ {
// Arrange // Arrange
var configuration = new ManagedAuthenticatedEncryptorConfiguration(new ManagedAuthenticatedEncryptionSettings()); var configuration = new ManagedAuthenticatedEncryptorConfiguration();
// Act // Act
var masterKey1 = ((ManagedAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey; var masterKey1 = ((ManagedAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor()).MasterKey;
@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void CreateNewDescriptor_PropagatesOptions() public void CreateNewDescriptor_PropagatesOptions()
{ {
// Arrange // Arrange
var configuration = new ManagedAuthenticatedEncryptorConfiguration(new ManagedAuthenticatedEncryptionSettings()); var configuration = new ManagedAuthenticatedEncryptorConfiguration();
// Act // Act
var descriptor = (ManagedAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor(); var descriptor = (ManagedAuthenticatedEncryptorDescriptor)configuration.CreateNewDescriptor();
// Assert // Assert
Assert.Equal(configuration.Settings, descriptor.Settings); Assert.Equal(configuration, descriptor.Configuration);
} }
} }
} }

View File

@ -4,6 +4,8 @@
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel 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) public void ImportFromXml_BuiltInTypes_CreatesAppropriateDescriptor(Type encryptionAlgorithmType, Type validationAlgorithmType)
{ {
// Arrange // Arrange
var control = new ManagedAuthenticatedEncryptorDescriptor( var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
new ManagedAuthenticatedEncryptionSettings() new ManagedAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithmType = encryptionAlgorithmType, EncryptionAlgorithmType = encryptionAlgorithmType,
EncryptionAlgorithmKeySize = 192, EncryptionAlgorithmKeySize = 192,
ValidationAlgorithmType = validationAlgorithmType ValidationAlgorithmType = validationAlgorithmType
}, },
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance(); "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
string xml = String.Format(@" string xml = string.Format(@"
<descriptor> <descriptor>
<encryption algorithm='{0}' keyLength='192' /> <encryption algorithm='{0}' keyLength='192' />
<validation algorithm='{1}' /> <validation algorithm='{1}' />
@ -36,7 +39,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
</masterKey> </masterKey>
</descriptor>", </descriptor>",
encryptionAlgorithmType.Name, validationAlgorithmType.Name); 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 // Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -50,16 +54,17 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_CustomType_CreatesAppropriateDescriptor() public void ImportFromXml_CustomType_CreatesAppropriateDescriptor()
{ {
// Arrange // Arrange
var control = new ManagedAuthenticatedEncryptorDescriptor( var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
new ManagedAuthenticatedEncryptionSettings() new ManagedAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithmType = typeof(Aes), EncryptionAlgorithmType = typeof(Aes),
EncryptionAlgorithmKeySize = 192, EncryptionAlgorithmKeySize = 192,
ValidationAlgorithmType = typeof(HMACSHA384) 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> <descriptor>
<encryption algorithm='{0}' keyLength='192' /> <encryption algorithm='{0}' keyLength='192' />
<validation algorithm='{1}' /> <validation algorithm='{1}' />
@ -68,7 +73,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
</masterKey> </masterKey>
</descriptor>", </descriptor>",
typeof(Aes).AssemblyQualifiedName, typeof(HMACSHA384).AssemblyQualifiedName); 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 // Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; 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)); byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext); 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);
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
// Arrange // Arrange
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret(); var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptionSettings() var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithmType = typeof(MySymmetricAlgorithm), EncryptionAlgorithmType = typeof(MySymmetricAlgorithm),
EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmKeySize = 2048,
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// Assert // Assert
Assert.Equal(typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer), retVal.DeserializerType); Assert.Equal(typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer), retVal.DeserializerType);
string expectedXml = String.Format(@" string expectedXml = string.Format(@"
<descriptor> <descriptor>
<encryption algorithm='{0}' keyLength='2048' /> <encryption algorithm='{0}' keyLength='2048' />
<validation algorithm='{1}' /> <validation algorithm='{1}' />
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
{ {
// Arrange // Arrange
var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret(); var masterKey = "k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret();
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptionSettings() var descriptor = new ManagedAuthenticatedEncryptorDescriptor(new ManagedAuthenticatedEncryptorConfiguration()
{ {
EncryptionAlgorithmType = encryptionAlgorithmType, EncryptionAlgorithmType = encryptionAlgorithmType,
EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmKeySize = 2048,
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
// Assert // Assert
Assert.Equal(typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer), retVal.DeserializerType); Assert.Equal(typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer), retVal.DeserializerType);
string expectedXml = String.Format(@" string expectedXml = string.Format(@"
<descriptor> <descriptor>
<encryption algorithm='{0}' keyLength='2048' /> <encryption algorithm='{0}' keyLength='2048' />
<validation algorithm='{1}' /> <validation algorithm='{1}' />

View File

@ -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);
}
}
}

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.DataProtection namespace Microsoft.AspNetCore.DataProtection
@ -14,8 +15,8 @@ namespace Microsoft.AspNetCore.DataProtection
public void DifferentProvider_SamePurpose_DoesNotRoundTripData() public void DifferentProvider_SamePurpose_DoesNotRoundTripData()
{ {
// Arrange // Arrange
var dataProtector1 = new EphemeralDataProtectionProvider().CreateProtector("purpose"); var dataProtector1 = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("purpose");
var dataProtector2 = new EphemeralDataProtectionProvider().CreateProtector("purpose"); var dataProtector2 = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("purpose");
byte[] bytes = Encoding.UTF8.GetBytes("Hello there!"); byte[] bytes = Encoding.UTF8.GetBytes("Hello there!");
// Act & assert // Act & assert
@ -31,7 +32,7 @@ namespace Microsoft.AspNetCore.DataProtection
public void SingleProvider_DifferentPurpose_DoesNotRoundTripData() public void SingleProvider_DifferentPurpose_DoesNotRoundTripData()
{ {
// Arrange // Arrange
var dataProtectionProvider = new EphemeralDataProtectionProvider(); var dataProtectionProvider = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance);
var dataProtector1 = dataProtectionProvider.CreateProtector("purpose"); var dataProtector1 = dataProtectionProvider.CreateProtector("purpose");
var dataProtector2 = dataProtectionProvider.CreateProtector("different purpose"); var dataProtector2 = dataProtectionProvider.CreateProtector("different purpose");
byte[] bytes = Encoding.UTF8.GetBytes("Hello there!"); byte[] bytes = Encoding.UTF8.GetBytes("Hello there!");
@ -48,7 +49,7 @@ namespace Microsoft.AspNetCore.DataProtection
public void SingleProvider_SamePurpose_RoundTripsData() public void SingleProvider_SamePurpose_RoundTripsData()
{ {
// Arrange // Arrange
var dataProtectionProvider = new EphemeralDataProtectionProvider(); var dataProtectionProvider = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance);
var dataProtector1 = dataProtectionProvider.CreateProtector("purpose"); var dataProtector1 = dataProtectionProvider.CreateProtector("purpose");
var dataProtector2 = dataProtectionProvider.CreateProtector("purpose"); // should be equivalent to the previous instance var dataProtector2 = dataProtectionProvider.CreateProtector("purpose"); // should be equivalent to the previous instance
byte[] bytes = Encoding.UTF8.GetBytes("Hello there!"); byte[] bytes = Encoding.UTF8.GetBytes("Hello there!");

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