[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

3
.gitignore vendored
View File

@ -27,4 +27,5 @@ nuget.exe
project.lock.json
.vs
.build/
.testPublish/
.testPublish/
samples/**/temp-keys/

View File

@ -1,51 +1,57 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26127.0
VisualStudioVersion = 15.0.26206.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection", "src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj", "{1E570CD4-6F12-44F4-961E-005EE2002BC2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{60336AB3-948D-4D15-A5FB-F32A2B91E814}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Test", "test\Microsoft.AspNetCore.DataProtection.Test\Microsoft.AspNetCore.DataProtection.Test.csproj", "{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.Internal", "src\Microsoft.AspNetCore.Cryptography.Internal\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{E2779976-A28C-4365-A4BB-4AD854FAF23E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation", "src\Microsoft.AspNetCore.Cryptography.KeyDerivation\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj", "{421F0383-34B1-402D-807B-A94542513ABA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation.Test", "test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test.csproj", "{42C97F52-8D56-46BD-A712-4F22BED157A7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Cryptography.Internal.Test", "test\Microsoft.AspNetCore.Cryptography.Internal.Test\Microsoft.AspNetCore.Cryptography.Internal.Test.csproj", "{37053D5F-5B61-47CE-8B72-298CE007FFB0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "src\Microsoft.AspNetCore.DataProtection.Abstractions\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{4B115BDE-B253-46A6-97BF-A8B37B344FF2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Abstractions.Test", "test\Microsoft.AspNetCore.DataProtection.Abstractions.Test\Microsoft.AspNetCore.DataProtection.Abstractions.Test.csproj", "{FF650A69-DEE4-4B36-9E30-264EE7CFB478}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.SystemWeb", "src\Microsoft.AspNetCore.DataProtection.SystemWeb\Microsoft.AspNetCore.DataProtection.SystemWeb.csproj", "{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Extensions.Test", "test\Microsoft.AspNetCore.DataProtection.Extensions.Test\Microsoft.AspNetCore.DataProtection.Extensions.Test.csproj", "{04AA8E60-A053-4D50-89FE-E76C3DF45200}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Extensions", "src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj", "{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Redis", "src\Microsoft.AspNetCore.DataProtection.Redis\Microsoft.AspNetCore.DataProtection.Redis.csproj", "{0508ADB0-9D2E-4506-9AA3-C15D7BEAE7C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.AzureStorage", "src\Microsoft.AspNetCore.DataProtection.AzureStorage\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj", "{CC799B57-81E2-4F45-8A32-0D5F49753C3F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5A3A5DE3-49AD-431C-971D-B01B62D94AE2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureBlob", "samples\AzureBlob\AzureBlob.csproj", "{B07435B3-CD81-4E3B-88A5-6384821E1C01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.Redis.Test", "test\Microsoft.AspNetCore.DataProtection.Redis.Test\Microsoft.AspNetCore.DataProtection.Redis.Test.csproj", "{ABCF00E5-5B2F-469C-90DC-908C5A04C08D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E1D86B1B-41D8-43C9-97FD-C2BF65C414E2}"
ProjectSection(SolutionItems) = preProject
NuGet.config = NuGet.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.DataProtection.AzureStorage.Test", "test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test.csproj", "{8C41240E-48F8-402F-9388-74CFE27F4D76}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection", "src\Microsoft.AspNetCore.DataProtection\Microsoft.AspNetCore.DataProtection.csproj", "{1E570CD4-6F12-44F4-961E-005EE2002BC2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Redis", "samples\Redis\Redis.csproj", "{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Test", "test\Microsoft.AspNetCore.DataProtection.Test\Microsoft.AspNetCore.DataProtection.Test.csproj", "{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.Internal", "src\Microsoft.AspNetCore.Cryptography.Internal\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{E2779976-A28C-4365-A4BB-4AD854FAF23E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation", "src\Microsoft.AspNetCore.Cryptography.KeyDerivation\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj", "{421F0383-34B1-402D-807B-A94542513ABA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.KeyDerivation.Test", "test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test\Microsoft.AspNetCore.Cryptography.KeyDerivation.Test.csproj", "{42C97F52-8D56-46BD-A712-4F22BED157A7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.Internal.Test", "test\Microsoft.AspNetCore.Cryptography.Internal.Test\Microsoft.AspNetCore.Cryptography.Internal.Test.csproj", "{37053D5F-5B61-47CE-8B72-298CE007FFB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "src\Microsoft.AspNetCore.DataProtection.Abstractions\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{4B115BDE-B253-46A6-97BF-A8B37B344FF2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions.Test", "test\Microsoft.AspNetCore.DataProtection.Abstractions.Test\Microsoft.AspNetCore.DataProtection.Abstractions.Test.csproj", "{FF650A69-DEE4-4B36-9E30-264EE7CFB478}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.SystemWeb", "src\Microsoft.AspNetCore.DataProtection.SystemWeb\Microsoft.AspNetCore.DataProtection.SystemWeb.csproj", "{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Extensions.Test", "test\Microsoft.AspNetCore.DataProtection.Extensions.Test\Microsoft.AspNetCore.DataProtection.Extensions.Test.csproj", "{04AA8E60-A053-4D50-89FE-E76C3DF45200}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Extensions", "src\Microsoft.AspNetCore.DataProtection.Extensions\Microsoft.AspNetCore.DataProtection.Extensions.csproj", "{BF8681DB-C28B-441F-BD92-0DCFE9537A9F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Redis", "src\Microsoft.AspNetCore.DataProtection.Redis\Microsoft.AspNetCore.DataProtection.Redis.csproj", "{0508ADB0-9D2E-4506-9AA3-C15D7BEAE7C9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureStorage", "src\Microsoft.AspNetCore.DataProtection.AzureStorage\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj", "{CC799B57-81E2-4F45-8A32-0D5F49753C3F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureBlob", "samples\AzureBlob\AzureBlob.csproj", "{B07435B3-CD81-4E3B-88A5-6384821E1C01}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Redis.Test", "test\Microsoft.AspNetCore.DataProtection.Redis.Test\Microsoft.AspNetCore.DataProtection.Redis.Test.csproj", "{ABCF00E5-5B2F-469C-90DC-908C5A04C08D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureStorage.Test", "test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test\Microsoft.AspNetCore.DataProtection.AzureStorage.Test.csproj", "{8C41240E-48F8-402F-9388-74CFE27F4D76}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Redis", "samples\Redis\Redis.csproj", "{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NonDISample", "samples\NonDISample\NonDISample.csproj", "{32CF970B-E2F1-4CD9-8DB3-F5715475373A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyManagementSample", "samples\KeyManagementSample\KeyManagementSample.csproj", "{6E066F8D-2910-404F-8949-F58125E28495}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomEncryptorSample", "samples\CustomEncryptorSample\CustomEncryptorSample.csproj", "{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -187,6 +193,30 @@ Global
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|Any CPU.Build.0 = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|x86.ActiveCfg = Release|Any CPU
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}.Release|x86.Build.0 = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|x86.ActiveCfg = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Debug|x86.Build.0 = Debug|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|Any CPU.Build.0 = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|x86.ActiveCfg = Release|Any CPU
{32CF970B-E2F1-4CD9-8DB3-F5715475373A}.Release|x86.Build.0 = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|x86.ActiveCfg = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Debug|x86.Build.0 = Debug|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|Any CPU.Build.0 = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|x86.ActiveCfg = Release|Any CPU
{6E066F8D-2910-404F-8949-F58125E28495}.Release|x86.Build.0 = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|x86.ActiveCfg = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Debug|x86.Build.0 = Debug|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|Any CPU.Build.0 = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|x86.ActiveCfg = Release|Any CPU
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -209,5 +239,8 @@ Global
{ABCF00E5-5B2F-469C-90DC-908C5A04C08D} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{8C41240E-48F8-402F-9388-74CFE27F4D76} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{32CF970B-E2F1-4CD9-8DB3-F5715475373A} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{6E066F8D-2910-404F-8949-F58125E28495} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
{F4D59BBD-6145-4EE0-BA6E-AD03605BF151} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
EndGlobalSection
EndGlobal

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.Extensions.DependencyInjection;
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.Extensions.DependencyInjection;
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.
using System;
@ -6,103 +6,90 @@ using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
{
/// <summary>
/// Settings for configuring authenticated encryption algorithms.
/// </summary>
public sealed class AuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
public sealed class AuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory
{
/// <summary>
/// The algorithm to use for symmetric encryption (confidentiality).
/// </summary>
/// <remarks>
/// The default value is <see cref="EncryptionAlgorithm.AES_256_CBC"/>.
/// </remarks>
public EncryptionAlgorithm EncryptionAlgorithm { get; set; } = EncryptionAlgorithm.AES_256_CBC;
private readonly ILoggerFactory _loggerFactory;
/// <summary>
/// The algorithm to use for message authentication (tamper-proofing).
/// </summary>
/// <remarks>
/// The default value is <see cref="ValidationAlgorithm.HMACSHA256"/>.
/// This property is ignored if <see cref="EncryptionAlgorithm"/> specifies a 'GCM' algorithm.
/// </remarks>
public ValidationAlgorithm ValidationAlgorithm { get; set; } = ValidationAlgorithm.HMACSHA256;
/// <summary>
/// Validates that this <see cref="AuthenticatedEncryptionSettings"/> is well-formed, i.e.,
/// that the specified algorithms actually exist and that they can be instantiated properly.
/// An exception will be thrown if validation fails.
/// </summary>
public void Validate()
public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory)
{
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8));
try
{
encryptor.PerformSelfTest();
}
finally
{
(encryptor as IDisposable)?.Dispose();
}
_loggerFactory = loggerFactory;
}
/*
* HELPER ROUTINES
*/
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, IServiceProvider services = null)
public IAuthenticatedEncryptor CreateEncryptorInstance(IKey key)
{
return CreateImplementationOptions()
.ToConfiguration(services)
.CreateDescriptorFromSecret(secret)
.CreateEncryptorInstance();
var descriptor = key.Descriptor as AuthenticatedEncryptorDescriptor;
if (descriptor == null)
{
return null;
}
return CreateAuthenticatedEncryptorInstance(descriptor.MasterKey, descriptor.Configuration);
}
private IInternalAuthenticatedEncryptionSettings CreateImplementationOptions()
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(
ISecret secret,
AuthenticatedEncryptorConfiguration authenticatedConfiguration)
{
if (IsGcmAlgorithm(EncryptionAlgorithm))
if (authenticatedConfiguration == null)
{
return null;
}
if (IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm))
{
// GCM requires CNG, and CNG is only supported on Windows.
if (!OSVersionUtil.IsWindows())
{
throw new PlatformNotSupportedException(Resources.Platform_WindowsRequiredForGcm);
}
return new CngGcmAuthenticatedEncryptionSettings()
var configuration = new CngGcmAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm)
EncryptionAlgorithm = GetBCryptAlgorithmNameFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm)
};
return new CngGcmAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
}
else
{
if (OSVersionUtil.IsWindows())
{
// CNG preferred over managed implementations if running on Windows
return new CngCbcAuthenticatedEncryptionSettings()
var configuration = new CngCbcAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm),
HashAlgorithm = GetBCryptAlgorithmName(ValidationAlgorithm)
EncryptionAlgorithm = GetBCryptAlgorithmNameFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm),
HashAlgorithm = GetBCryptAlgorithmNameFromValidationAlgorithm(authenticatedConfiguration.ValidationAlgorithm)
};
return new CngCbcAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
}
else
{
// Use managed implementations as a fallback
return new ManagedAuthenticatedEncryptionSettings()
var configuration = new ManagedAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithmType = GetManagedTypeForAlgorithm(EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm),
ValidationAlgorithmType = GetManagedTypeForAlgorithm(ValidationAlgorithm)
EncryptionAlgorithmType = GetManagedTypeFromEncryptionAlgorithm(authenticatedConfiguration.EncryptionAlgorithm),
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm),
ValidationAlgorithmType = GetManagedTypeFromValidationAlgorithm(authenticatedConfiguration.ValidationAlgorithm)
};
return new ManagedAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration);
}
}
}
internal static bool IsGcmAlgorithm(EncryptionAlgorithm algorithm)
{
return (EncryptionAlgorithm.AES_128_GCM <= algorithm && algorithm <= EncryptionAlgorithm.AES_256_GCM);
}
private static int GetAlgorithmKeySizeInBits(EncryptionAlgorithm algorithm)
{
switch (algorithm)
@ -120,11 +107,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return 256;
default:
throw new ArgumentOutOfRangeException(nameof(algorithm));
throw new ArgumentOutOfRangeException(nameof(EncryptionAlgorithm));
}
}
private static string GetBCryptAlgorithmName(EncryptionAlgorithm algorithm)
private static string GetBCryptAlgorithmNameFromEncryptionAlgorithm(EncryptionAlgorithm algorithm)
{
switch (algorithm)
{
@ -137,11 +124,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return Constants.BCRYPT_AES_ALGORITHM;
default:
throw new ArgumentOutOfRangeException(nameof(algorithm));
throw new ArgumentOutOfRangeException(nameof(EncryptionAlgorithm));
}
}
private static string GetBCryptAlgorithmName(ValidationAlgorithm algorithm)
private static string GetBCryptAlgorithmNameFromValidationAlgorithm(ValidationAlgorithm algorithm)
{
switch (algorithm)
{
@ -152,11 +139,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return Constants.BCRYPT_SHA512_ALGORITHM;
default:
throw new ArgumentOutOfRangeException(nameof(algorithm));
throw new ArgumentOutOfRangeException(nameof(ValidationAlgorithm));
}
}
private static Type GetManagedTypeForAlgorithm(EncryptionAlgorithm algorithm)
private static Type GetManagedTypeFromEncryptionAlgorithm(EncryptionAlgorithm algorithm)
{
switch (algorithm)
{
@ -169,11 +156,11 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return typeof(Aes);
default:
throw new ArgumentOutOfRangeException(nameof(algorithm));
throw new ArgumentOutOfRangeException(nameof(EncryptionAlgorithm));
}
}
private static Type GetManagedTypeForAlgorithm(ValidationAlgorithm algorithm)
private static Type GetManagedTypeFromValidationAlgorithm(ValidationAlgorithm algorithm)
{
switch (algorithm)
{
@ -184,18 +171,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
return typeof(HMACSHA512);
default:
throw new ArgumentOutOfRangeException(nameof(algorithm));
throw new ArgumentOutOfRangeException(nameof(ValidationAlgorithm));
}
}
internal static bool IsGcmAlgorithm(EncryptionAlgorithm algorithm)
{
return (EncryptionAlgorithm.AES_128_GCM <= algorithm && algorithm <= EncryptionAlgorithm.AES_256_GCM);
}
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionSettings.ToConfiguration(IServiceProvider services)
{
return new AuthenticatedEncryptorConfiguration(this, services);
}
}
}

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

View File

@ -2,42 +2,57 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{
/// <summary>
/// Represents a generalized authenticated encryption mechanism.
/// </summary>
public sealed class AuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
public sealed class AuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
{
private readonly IServiceProvider _services;
/// <summary>
/// The algorithm to use for symmetric encryption (confidentiality).
/// </summary>
/// <remarks>
/// The default value is <see cref="EncryptionAlgorithm.AES_256_CBC"/>.
/// </remarks>
public EncryptionAlgorithm EncryptionAlgorithm { get; set; } = EncryptionAlgorithm.AES_256_CBC;
public AuthenticatedEncryptorConfiguration(AuthenticatedEncryptionSettings settings)
: this(settings, services: null)
/// <summary>
/// The algorithm to use for message authentication (tamper-proofing).
/// </summary>
/// <remarks>
/// The default value is <see cref="ValidationAlgorithm.HMACSHA256"/>.
/// This property is ignored if <see cref="EncryptionAlgorithm"/> specifies a 'GCM' algorithm.
/// </remarks>
public ValidationAlgorithm ValidationAlgorithm { get; set; } = ValidationAlgorithm.HMACSHA256;
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
}
public AuthenticatedEncryptorConfiguration(AuthenticatedEncryptionSettings settings, IServiceProvider services)
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
if (settings == null)
return new AuthenticatedEncryptorDescriptor(this, secret);
}
void IInternalAlgorithmConfiguration.Validate()
{
var factory = new AuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this);
try
{
throw new ArgumentNullException(nameof(settings));
encryptor.PerformSelfTest();
}
finally
{
(encryptor as IDisposable)?.Dispose();
}
Settings = settings;
_services = services;
}
public AuthenticatedEncryptionSettings Settings { get; }
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
return this.CreateNewDescriptorCore();
}
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
return new AuthenticatedEncryptorDescriptor(Settings, secret, _services);
}
}
}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Cryptography;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{
@ -9,36 +9,91 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// Represents a configured authenticated encryption mechanism which uses
/// Windows CNG algorithms in CBC encryption + HMAC authentication modes.
/// </summary>
public sealed class CngCbcAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
public sealed class CngCbcAuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
{
private readonly IServiceProvider _services;
/// <summary>
/// The name of the algorithm to use for symmetric encryption.
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The algorithm must support CBC-style encryption and must have a block size of 64 bits
/// or greater.
/// The default value is 'AES'.
/// </remarks>
[ApplyPolicy]
public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
public CngCbcAuthenticatedEncryptorConfiguration(CngCbcAuthenticatedEncryptionSettings settings)
: this(settings, services: null)
/// <summary>
/// The name of the provider which contains the implementation of the symmetric encryption algorithm.
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
/// This property is optional.
/// </summary>
/// <remarks>
/// The default value is null.
/// </remarks>
[ApplyPolicy]
public string EncryptionAlgorithmProvider { get; set; } = null;
/// <summary>
/// The length (in bits) of the key that will be used for symmetric encryption.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The key length must be 128 bits or greater.
/// The default value is 256.
/// </remarks>
[ApplyPolicy]
public int EncryptionAlgorithmKeySize { get; set; } = 256;
/// <summary>
/// The name of the algorithm to use for hashing data.
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The algorithm must support being opened in HMAC mode and must have a digest length
/// of 128 bits or greater.
/// The default value is 'SHA256'.
/// </remarks>
[ApplyPolicy]
public string HashAlgorithm { get; set; } = Constants.BCRYPT_SHA256_ALGORITHM;
/// <summary>
/// The name of the provider which contains the implementation of the hash algorithm.
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
/// This property is optional.
/// </summary>
/// <remarks>
/// The default value is null.
/// </remarks>
[ApplyPolicy]
public string HashAlgorithmProvider { get; set; } = null;
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
}
public CngCbcAuthenticatedEncryptorConfiguration(CngCbcAuthenticatedEncryptionSettings settings, IServiceProvider services)
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
if (settings == null)
return new CngCbcAuthenticatedEncryptorDescriptor(this, secret);
}
/// <summary>
/// Validates that this <see cref="CngCbcAuthenticatedEncryptorConfiguration"/> is well-formed, i.e.,
/// that the specified algorithms actually exist and that they can be instantiated properly.
/// An exception will be thrown if validation fails.
/// </summary>
void IInternalAlgorithmConfiguration.Validate()
{
var factory = new CngCbcAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this))
{
throw new ArgumentNullException(nameof(settings));
encryptor.PerformSelfTest();
}
Settings = settings;
_services = services;
}
public CngCbcAuthenticatedEncryptionSettings Settings { get; }
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
return this.CreateNewDescriptorCore();
}
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
return new CngCbcAuthenticatedEncryptorDescriptor(Settings, secret, _services);
}
}
}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Cryptography;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{
@ -9,36 +9,67 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// Represents a configured authenticated encryption mechanism which uses
/// Windows CNG algorithms in GCM encryption + authentication modes.
/// </summary>
public sealed class CngGcmAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
public sealed class CngGcmAuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
{
private readonly IServiceProvider _services;
/// <summary>
/// The name of the algorithm to use for symmetric encryption.
/// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The algorithm must support GCM-style encryption and must have a block size exactly
/// 128 bits.
/// The default value is 'AES'.
/// </remarks>
[ApplyPolicy]
public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
public CngGcmAuthenticatedEncryptorConfiguration(CngGcmAuthenticatedEncryptionSettings settings)
: this(settings, services: null)
/// <summary>
/// The name of the provider which contains the implementation of the symmetric encryption algorithm.
/// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
/// This property is optional.
/// </summary>
/// <remarks>
/// The default value is null.
/// </remarks>
[ApplyPolicy]
public string EncryptionAlgorithmProvider { get; set; } = null;
/// <summary>
/// The length (in bits) of the key that will be used for symmetric encryption.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The key length must be 128 bits or greater.
/// The default value is 256.
/// </remarks>
[ApplyPolicy]
public int EncryptionAlgorithmKeySize { get; set; } = 256;
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
}
public CngGcmAuthenticatedEncryptorConfiguration(CngGcmAuthenticatedEncryptionSettings settings, IServiceProvider services)
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
if (settings == null)
return new CngGcmAuthenticatedEncryptorDescriptor(this, secret);
}
/// <summary>
/// Validates that this <see cref="CngGcmAuthenticatedEncryptorConfiguration"/> is well-formed, i.e.,
/// that the specified algorithm actually exists and can be instantiated properly.
/// An exception will be thrown if validation fails.
/// </summary>
void IInternalAlgorithmConfiguration.Validate()
{
var factory = new CngGcmAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this))
{
throw new ArgumentNullException(nameof(settings));
encryptor.PerformSelfTest();
}
Settings = settings;
_services = services;
}
public CngGcmAuthenticatedEncryptionSettings Settings { get; }
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
return this.CreateNewDescriptorCore();
}
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
return new CngGcmAuthenticatedEncryptorDescriptor(Settings, secret, _services);
}
}
}

View File

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

View File

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

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>
public interface IAuthenticatedEncryptorDescriptor
{
/// <summary>
/// Creates an <see cref="IAuthenticatedEncryptor"/> instance based on the current descriptor.
/// </summary>
/// <returns>An <see cref="IAuthenticatedEncryptor"/> instance.</returns>
/// <remarks>
/// For a given descriptor, any two instances returned by this method should
/// be considered equivalent, e.g., the payload returned by one's <see cref="IAuthenticatedEncryptor.Encrypt(ArraySegment{byte}, ArraySegment{byte})"/>
/// method should be consumable by the other's <see cref="IAuthenticatedEncryptor.Decrypt(ArraySegment{byte}, ArraySegment{byte})"/> method.
/// </remarks>
IAuthenticatedEncryptor CreateEncryptorInstance();
/// <summary>
/// Exports the current descriptor to XML.
/// </summary>

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.
using System;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{
// This type is not public because we don't want to lock ourselves into a contract stating
// that a descriptor is simply a configuration plus a single serializable, reproducible secret.
/// <summary>
/// A type that knows how to create instances of an <see cref="IAuthenticatedEncryptorDescriptor"/>
/// given specific secret key material.
/// </summary>
internal interface IInternalAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration
/// <remarks>
/// This type is not public because we don't want to lock ourselves into a contract stating
/// that a descriptor is simply a configuration plus a single serializable, reproducible secret.
/// </remarks>
internal interface IInternalAlgorithmConfiguration
{
/// <summary>
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this
/// configuration given specific secret key material.
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this configuration
/// given specific secret key material.
/// </summary>
IAuthenticatedEncryptorDescriptor CreateDescriptorFromSecret(ISecret secret);
/// <summary>
/// Performs a self-test of the algorithm specified by the configuration object.
/// </summary>
void Validate();
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Cryptography;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
{
@ -10,36 +11,97 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
/// managed <see cref="System.Security.Cryptography.SymmetricAlgorithm"/> and
/// <see cref="System.Security.Cryptography.KeyedHashAlgorithm"/> types.
/// </summary>
public sealed class ManagedAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
public sealed class ManagedAuthenticatedEncryptorConfiguration : AlgorithmConfiguration, IInternalAlgorithmConfiguration
{
private readonly IServiceProvider _services;
/// <summary>
/// The type of the algorithm to use for symmetric encryption.
/// The type must subclass <see cref="SymmetricAlgorithm"/>.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The algorithm must support CBC-style encryption and PKCS#7 padding and must have a block size of 64 bits or greater.
/// The default algorithm is AES.
/// </remarks>
[ApplyPolicy]
public Type EncryptionAlgorithmType { get; set; } = typeof(Aes);
public ManagedAuthenticatedEncryptorConfiguration(ManagedAuthenticatedEncryptionSettings settings)
: this(settings, services: null)
/// <summary>
/// The length (in bits) of the key that will be used for symmetric encryption.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The key length must be 128 bits or greater.
/// The default value is 256.
/// </remarks>
[ApplyPolicy]
public int EncryptionAlgorithmKeySize { get; set; } = 256;
/// <summary>
/// The type of the algorithm to use for validation.
/// Type type must subclass <see cref="KeyedHashAlgorithm"/>.
/// This property is required to have a value.
/// </summary>
/// <remarks>
/// The algorithm must have a digest length of 128 bits or greater.
/// The default algorithm is HMACSHA256.
/// </remarks>
[ApplyPolicy]
public Type ValidationAlgorithmType { get; set; } = typeof(HMACSHA256);
public override IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
{
var internalConfiguration = (IInternalAlgorithmConfiguration)this;
return internalConfiguration.CreateDescriptorFromSecret(Secret.Random(KDK_SIZE_IN_BYTES));
}
public ManagedAuthenticatedEncryptorConfiguration(ManagedAuthenticatedEncryptionSettings settings, IServiceProvider services)
IAuthenticatedEncryptorDescriptor IInternalAlgorithmConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
if (settings == null)
return new ManagedAuthenticatedEncryptorDescriptor(this, secret);
}
/// <summary>
/// Validates that this <see cref="ManagedAuthenticatedEncryptorConfiguration"/> is well-formed, i.e.,
/// that the specified algorithms actually exist and can be instantiated properly.
/// An exception will be thrown if validation fails.
/// </summary>
void IInternalAlgorithmConfiguration.Validate()
{
var factory = new ManagedAuthenticatedEncryptorFactory(DataProtectionProviderFactory.GetDefaultLoggerFactory());
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
using (var encryptor = factory.CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8), this))
{
throw new ArgumentNullException(nameof(settings));
encryptor.PerformSelfTest();
}
Settings = settings;
_services = services;
}
public ManagedAuthenticatedEncryptionSettings Settings { get; }
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
// Any changes to this method should also be be reflected
// in ManagedAuthenticatedEncryptorDescriptorDeserializer.FriendlyNameToType.
private static string TypeToFriendlyName(Type type)
{
return this.CreateNewDescriptorCore();
}
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
{
return new ManagedAuthenticatedEncryptorDescriptor(Settings, secret, _services);
if (type == typeof(Aes))
{
return nameof(Aes);
}
else if (type == typeof(HMACSHA1))
{
return nameof(HMACSHA1);
}
else if (type == typeof(HMACSHA256))
{
return nameof(HMACSHA256);
}
else if (type == typeof(HMACSHA384))
{
return nameof(HMACSHA384);
}
else if (type == typeof(HMACSHA512))
{
return nameof(HMACSHA512);
}
else
{
return type.AssemblyQualifiedName;
}
}
}
}

View File

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

View File

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

View File

@ -1,8 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
{
/// <summary>

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.ComponentModel;
using System.IO;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Win32;
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
@ -68,7 +73,11 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(sink));
}
builder.Services.AddSingleton<IKeyEscrowSink>(sink);
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.KeyEscrowSinks.Add(sink);
});
return builder;
}
@ -89,7 +98,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(builder));
}
builder.Services.AddSingleton<IKeyEscrowSink, TImplementation>();
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var implementationInstance = services.GetRequiredService<TImplementation>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.KeyEscrowSinks.Add(implementationInstance);
});
});
return builder;
}
@ -114,7 +131,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(factory));
}
builder.Services.AddSingleton<IKeyEscrowSink>(factory);
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var instance = factory(services);
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.KeyEscrowSinks.Add(instance);
});
});
return builder;
}
@ -182,7 +207,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(directory));
}
Use(builder.Services, DataProtectionServiceDescriptors.IXmlRepository_FileSystem(directory));
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.XmlRepository = new FileSystemXmlRepository(directory, loggerFactory);
});
});
return builder;
}
@ -204,7 +237,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(registryKey));
}
Use(builder.Services, DataProtectionServiceDescriptors.IXmlRepository_Registry(registryKey));
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.XmlRepository = new RegistryXmlRepository(registryKey, loggerFactory);
});
});
return builder;
}
@ -228,7 +269,15 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(certificate));
}
Use(builder.Services, DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(certificate));
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.XmlEncryptor = new CertificateXmlEncryptor(certificate, loggerFactory);
});
});
return builder;
}
@ -256,12 +305,20 @@ namespace Microsoft.AspNetCore.DataProtection
throw Error.CertificateXmlEncryptor_CertificateNotFound(thumbprint);
}
var services = builder.Services;
// ICertificateResolver is necessary for this type to work correctly, so register it
// if it doesn't already exist.
services.TryAdd(DataProtectionServiceDescriptors.ICertificateResolver_Default());
Use(services, DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(thumbprint));
builder.Services.TryAddSingleton<ICertificateResolver, CertificateResolver>();
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
var certificateResolver = services.GetRequiredService<ICertificateResolver>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
options.XmlEncryptor = new CertificateXmlEncryptor(thumbprint, certificateResolver, loggerFactory);
});
});
return builder;
}
@ -305,7 +362,16 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(builder));
}
Use(builder.Services, DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToLocalMachine));
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
CryptoUtil.AssertPlatformIsWindows();
options.XmlEncryptor = new DpapiXmlEncryptor(protectToLocalMachine, loggerFactory);
});
});
return builder;
}
@ -358,7 +424,16 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(protectionDescriptorRule));
}
Use(builder.Services, DataProtectionServiceDescriptors.IXmlEncryptor_DpapiNG(protectionDescriptorRule, flags));
builder.Services.AddSingleton<IConfigureOptions<KeyManagementOptions>>(services =>
{
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
return new ConfigureOptions<KeyManagementOptions>(options =>
{
CryptoUtil.AssertPlatformIsWindows8OrLater();
options.XmlEncryptor = new DpapiNGXmlEncryptor(protectionDescriptorRule, flags, loggerFactory);
});
});
return builder;
}
@ -395,21 +470,21 @@ namespace Microsoft.AspNetCore.DataProtection
/// by default when generating protected payloads.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
public static IDataProtectionBuilder UseCryptographicAlgorithms(this IDataProtectionBuilder builder, AuthenticatedEncryptionSettings settings)
public static IDataProtectionBuilder UseCryptographicAlgorithms(this IDataProtectionBuilder builder, AuthenticatedEncryptorConfiguration configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (settings == null)
if (configuration == null)
{
throw new ArgumentNullException(nameof(settings));
throw new ArgumentNullException(nameof(configuration));
}
return UseCryptographicAlgorithmsCore(builder, settings);
return UseCryptographicAlgorithmsCore(builder, configuration);
}
/// <summary>
@ -419,25 +494,25 @@ namespace Microsoft.AspNetCore.DataProtection
/// <see cref="ValidationAlgorithm"/> enumerations.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
/// <remarks>
/// This API is only available on Windows.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngCbcAuthenticatedEncryptionSettings settings)
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngCbcAuthenticatedEncryptorConfiguration configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (settings == null)
if (configuration == null)
{
throw new ArgumentNullException(nameof(settings));
throw new ArgumentNullException(nameof(configuration));
}
return UseCryptographicAlgorithmsCore(builder, settings);
return UseCryptographicAlgorithmsCore(builder, configuration);
}
/// <summary>
@ -447,25 +522,25 @@ namespace Microsoft.AspNetCore.DataProtection
/// <see cref="ValidationAlgorithm"/> enumerations.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
/// <remarks>
/// This API is only available on Windows.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngGcmAuthenticatedEncryptionSettings settings)
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, CngGcmAuthenticatedEncryptorConfiguration configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (settings == null)
if (configuration == null)
{
throw new ArgumentNullException(nameof(settings));
throw new ArgumentNullException(nameof(configuration));
}
return UseCryptographicAlgorithmsCore(builder, settings);
return UseCryptographicAlgorithmsCore(builder, configuration);
}
/// <summary>
@ -475,28 +550,33 @@ namespace Microsoft.AspNetCore.DataProtection
/// <see cref="ValidationAlgorithm"/> enumerations.
/// </summary>
/// <param name="builder">The <see cref="IDataProtectionBuilder"/>.</param>
/// <param name="settings">Information about what cryptographic algorithms should be used.</param>
/// <param name="configuration">Information about what cryptographic algorithms should be used.</param>
/// <returns>A reference to the <see cref="IDataProtectionBuilder" /> after this operation has completed.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, ManagedAuthenticatedEncryptionSettings settings)
public static IDataProtectionBuilder UseCustomCryptographicAlgorithms(this IDataProtectionBuilder builder, ManagedAuthenticatedEncryptorConfiguration configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (settings == null)
if (configuration == null)
{
throw new ArgumentNullException(nameof(settings));
throw new ArgumentNullException(nameof(configuration));
}
return UseCryptographicAlgorithmsCore(builder, settings);
return UseCryptographicAlgorithmsCore(builder, configuration);
}
private static IDataProtectionBuilder UseCryptographicAlgorithmsCore(IDataProtectionBuilder builder, IInternalAuthenticatedEncryptionSettings settings)
private static IDataProtectionBuilder UseCryptographicAlgorithmsCore(IDataProtectionBuilder builder, AlgorithmConfiguration configuration)
{
settings.Validate(); // perform self-test
Use(builder.Services, DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(settings));
((IInternalAlgorithmConfiguration)configuration).Validate(); // perform self-test
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.AuthenticatedEncryptorConfiguration = configuration;
});
return builder;
}
@ -517,30 +597,9 @@ namespace Microsoft.AspNetCore.DataProtection
throw new ArgumentNullException(nameof(builder));
}
Use(builder.Services, DataProtectionServiceDescriptors.IDataProtectionProvider_Ephemeral());
builder.Services.Replace(ServiceDescriptor.Singleton<IDataProtectionProvider, EphemeralDataProtectionProvider>());
return builder;
}
/*
* UTILITY ISERVICECOLLECTION METHODS
*/
private static void RemoveAllServicesOfType(IServiceCollection services, Type serviceType)
{
// We go backward since we're modifying the collection in-place.
for (var i = services.Count - 1; i >= 0; i--)
{
if (services[i]?.ServiceType == serviceType)
{
services.RemoveAt(i);
}
}
}
private static void Use(IServiceCollection services, ServiceDescriptor descriptor)
{
RemoveAllServicesOfType(services, descriptor.ServiceType);
services.Add(descriptor);
}
}
}

View File

@ -1,79 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.DataProtection
{
/// <summary>
/// Contains static factory methods for creating <see cref="IDataProtectionProvider"/> instances.
/// </summary>
internal static class DataProtectionProviderFactory
{
/// <summary>
/// Creates an <see cref="IDataProtectionProvider"/> given an <see cref="IServiceProvider"/>.
/// </summary>
/// <param name="options">The global options to use when creating the provider.</param>
/// <param name="services">Provides mandatory services for use by the provider.</param>
/// <returns>An <see cref="IDataProtectionProvider"/>.</returns>
public static IDataProtectionProvider GetProviderFromServices(DataProtectionOptions options, IServiceProvider services)
public static ILoggerFactory GetDefaultLoggerFactory()
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
return GetProviderFromServices(options, services, mustCreateImmediately: false);
}
internal static IDataProtectionProvider GetProviderFromServices(DataProtectionOptions options, IServiceProvider services, bool mustCreateImmediately)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
IDataProtectionProvider dataProtectionProvider = null;
// If we're being asked to create the provider immediately, then it means that
// we're already in a call to GetService, and we're responsible for supplying
// the default implementation ourselves. We can't call GetService again or
// else we risk stack diving.
if (!mustCreateImmediately)
{
dataProtectionProvider = services.GetService<IDataProtectionProvider>();
}
// If all else fails, create a keyring manually based on the other registered services.
if (dataProtectionProvider == null)
{
var keyRingProvider = new KeyRingProvider(
keyManager: services.GetRequiredService<IKeyManager>(),
keyManagementOptions: services.GetService<IOptions<KeyManagementOptions>>()?.Value, // might be null
services: services);
dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyRingProvider, services);
}
// Finally, link the provider to the supplied discriminator
if (!String.IsNullOrEmpty(options.ApplicationDiscriminator))
{
dataProtectionProvider = dataProtectionProvider.CreateProtector(options.ApplicationDiscriminator);
}
return dataProtectionProvider;
return NullLoggerFactory.Instance;
}
}
}

View File

@ -2,9 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.Internal;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
@ -26,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton<IActivator, RC1ForwardingActivator>();
services.AddOptions();
services.TryAdd(DataProtectionServices.GetDefaultServices());
AddDataProtectionServices(services);
return new DataProtectionBuilder(services);
}
@ -53,5 +59,48 @@ namespace Microsoft.Extensions.DependencyInjection
services.Configure(setupAction);
return builder;
}
private static void AddDataProtectionServices(IServiceCollection services)
{
services.TryAddSingleton<ILoggerFactory>(DataProtectionProviderFactory.GetDefaultLoggerFactory());
if (OSVersionUtil.IsWindows())
{
services.AddSingleton<RegistryPolicyResolver>();
}
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IConfigureOptions<KeyManagementOptions>, KeyManagementOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<DataProtectionOptions>, DataProtectionOptionsSetup>());
services.AddSingleton<IKeyManager, XmlKeyManager>();
// Internal services
services.AddSingleton<IDefaultKeyResolver, DefaultKeyResolver>();
services.AddSingleton<IKeyRingProvider, KeyRingProvider>();
services.AddSingleton<IDataProtectionProvider>(s =>
{
var dpOptions = s.GetRequiredService<IOptions<DataProtectionOptions>>();
var keyRingProvider = s.GetRequiredService<IKeyRingProvider>();
var loggerFactory = s.GetRequiredService<ILoggerFactory>();
IDataProtectionProvider dataProtectionProvider = null;
dataProtectionProvider = new KeyRingBasedDataProtectionProvider(keyRingProvider, loggerFactory);
// Link the provider to the supplied discriminator
if (!string.IsNullOrEmpty(dpOptions.Value.ApplicationDiscriminator))
{
dataProtectionProvider = dataProtectionProvider.CreateProtector(dpOptions.Value.ApplicationDiscriminator);
}
return dataProtectionProvider;
});
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
services.AddSingleton<ICertificateResolver, CertificateResolver>();
#endif
}
}
}

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

View File

@ -39,13 +39,13 @@ namespace Microsoft.AspNetCore.DataProtection
public static InvalidOperationException Common_PropertyCannotBeNullOrEmpty(string propertyName)
{
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyCannotBeNullOrEmpty, propertyName);
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyCannotBeNullOrEmpty, propertyName);
return new InvalidOperationException(message);
}
public static InvalidOperationException Common_PropertyMustBeNonNegative(string propertyName)
{
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyMustBeNonNegative, propertyName);
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyMustBeNonNegative, propertyName);
return new InvalidOperationException(message);
}
@ -56,13 +56,13 @@ namespace Microsoft.AspNetCore.DataProtection
public static CryptographicException Common_KeyNotFound(Guid id)
{
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyNotFound, id);
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_KeyNotFound, id);
return new CryptographicException(message);
}
public static CryptographicException Common_KeyRevoked(Guid id)
{
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyRevoked, id);
var message = string.Format(CultureInfo.CurrentCulture, Resources.Common_KeyRevoked, id);
return new CryptographicException(message);
}
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.DataProtection
public static InvalidOperationException XmlKeyManager_DuplicateKey(Guid keyId)
{
var message = String.Format(CultureInfo.CurrentCulture, Resources.XmlKeyManager_DuplicateKey, keyId);
var message = string.Format(CultureInfo.CurrentCulture, Resources.XmlKeyManager_DuplicateKey, keyId);
return new InvalidOperationException(message);
}
}

View File

@ -1,9 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using System.IO;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.DataProtection
{
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.DataProtection
/// <para>
/// Similarly, when a developer modifies the default protected payload cryptographic
/// algorithms, it is intended that he also select an explitiy key storage location.
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptionSettings)"/>
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptorConfiguration)"/>
/// should therefore generally be paired with a call to <see cref="DataProtectionBuilderExtensions.PersistKeysToFileSystem(IDataProtectionBuilder,DirectoryInfo)"/>,
/// for example.
/// </para>
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.DataProtection
/// <para>
/// Similarly, when a developer modifies the default protected payload cryptographic
/// algorithms, it is intended that he also select an explitiy key storage location.
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptionSettings)"/>
/// A call to <see cref="DataProtectionBuilderExtensions.UseCryptographicAlgorithms(IDataProtectionBuilder,AuthenticatedEncryptorConfiguration)"/>
/// should therefore generally be paired with a call to <see cref="DataProtectionBuilderExtensions.PersistKeysToFileSystem(IDataProtectionBuilder,DirectoryInfo)"/>,
/// for example.
/// </para>

View File

@ -2,18 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.ComponentModel;
using System.IO;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Win32;
#if !DOTNET5_4 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
using System.Security.Cryptography.X509Certificates;
#endif
namespace Microsoft.AspNetCore.DataProtection.Internal
{

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

View File

@ -3,7 +3,6 @@
using System;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
@ -23,11 +22,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
DateTimeOffset expirationDate,
IInternalXmlKeyManager keyManager,
XElement keyElement)
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptor>(GetLazyEncryptorDelegate(keyManager, keyElement)))
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptorDescriptor>(GetLazyDescriptorDelegate(keyManager, keyElement)))
{
}
private static Func<IAuthenticatedEncryptor> GetLazyEncryptorDelegate(IInternalXmlKeyManager keyManager, XElement keyElement)
private static Func<IAuthenticatedEncryptorDescriptor> GetLazyDescriptorDelegate(IInternalXmlKeyManager keyManager, XElement keyElement)
{
// The <key> element will be held around in memory for a potentially lengthy period
// of time. Since it might contain sensitive information, we should protect it.
@ -35,7 +34,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
try
{
return () => keyManager.DeserializeDescriptorFromKeyElement(encryptedKeyElement.ToXElement()).CreateEncryptorInstance();
return () => keyManager.DeserializeDescriptorFromKeyElement(encryptedKeyElement.ToXElement());
}
finally
{

View File

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

View File

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

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
{
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
/// The default key, may be null if no key is a good default candidate.
/// </summary>
/// <remarks>
/// If this property is non-null, its <see cref="IKey.CreateEncryptorInstance"/> method will succeed
/// If this property is non-null, its <see cref="IAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)"/> method will succeed
/// so is appropriate for use with deferred keys.
/// </remarks>
public IKey DefaultKey;
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
/// be null if there is no viable fallback key.
/// </summary>
/// <remarks>
/// If this property is non-null, its <see cref="IKey.CreateEncryptorInstance"/> method will succeed
/// If this property is non-null, its <see cref="IAuthenticatedEncryptorFactory.CreateEncryptorInstance(IKey)"/> method will succeed
/// so is appropriate for use with deferred keys.
/// </remarks>
public IKey FallbackKey;

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
internal sealed class Key : KeyBase
{
public Key(Guid keyId, DateTimeOffset creationDate, DateTimeOffset activationDate, DateTimeOffset expirationDate, IAuthenticatedEncryptorDescriptor descriptor)
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptor>(descriptor.CreateEncryptorInstance))
: base(keyId, creationDate, activationDate, expirationDate, new Lazy<IAuthenticatedEncryptorDescriptor>(() => descriptor))
{
}
}

View File

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

View File

@ -2,6 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
@ -24,8 +29,21 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
if (other != null)
{
this.AutoGenerateKeys = other.AutoGenerateKeys;
this._newKeyLifetime = other._newKeyLifetime;
AutoGenerateKeys = other.AutoGenerateKeys;
_newKeyLifetime = other._newKeyLifetime;
XmlEncryptor = other.XmlEncryptor;
XmlRepository = other.XmlRepository;
AuthenticatedEncryptorConfiguration = other.AuthenticatedEncryptorConfiguration;
foreach (var keyEscrowSink in other.KeyEscrowSinks)
{
KeyEscrowSinks.Add(keyEscrowSink);
}
foreach (var encryptorFactory in other.AuthenticatedEncryptorFactories)
{
AuthenticatedEncryptorFactories.Add(encryptorFactory);
}
}
}
@ -119,5 +137,32 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
_newKeyLifetime = value;
}
}
/// <summary>
/// The <see cref="AlgorithmConfiguration"/> instance that can be used to create
/// the <see cref="IAuthenticatedEncryptorDescriptor"/> instance.
/// </summary>
public AlgorithmConfiguration AuthenticatedEncryptorConfiguration { get; set; }
/// <summary>
/// The list of <see cref="IKeyEscrowSink"/> to store the key material in.
/// </summary>
public IList<IKeyEscrowSink> KeyEscrowSinks { get; } = new List<IKeyEscrowSink>();
/// <summary>
/// The <see cref="IXmlRepository"/> to use for storing and retrieving XML elements.
/// </summary>
public IXmlRepository XmlRepository { get; set; }
/// <summary>
/// The <see cref="IXmlEncryptor"/> to use for encrypting XML elements.
/// </summary>
public IXmlEncryptor XmlEncryptor { get; set; }
/// <summary>
/// The list of <see cref="IAuthenticatedEncryptorFactory"/> that will be used for creating
/// <see cref="IAuthenticatedEncryptor"/>s.
/// </summary>
public IList<IAuthenticatedEncryptorFactory> AuthenticatedEncryptorFactories { get; } = new List<IAuthenticatedEncryptorFactory>();
}
}

View File

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

View File

@ -12,10 +12,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private readonly IKeyRingProvider _keyRingProvider;
private readonly ILogger _logger;
public KeyRingBasedDataProtectionProvider(IKeyRingProvider keyRingProvider, IServiceProvider services)
public KeyRingBasedDataProtectionProvider(IKeyRingProvider keyRingProvider, ILoggerFactory loggerFactory)
{
_keyRingProvider = keyRingProvider;
_logger = services.GetLogger<KeyRingBasedDataProtector>(); // note: for protector (not provider!) type, could be null
_logger = loggerFactory.CreateLogger<KeyRingBasedDataProtector>(); // note: for protector (not provider!) type
}
public IDataProtector CreateProtector(string purpose)

View File

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

View File

@ -6,9 +6,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
@ -22,14 +23,32 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private readonly IKeyManager _keyManager;
private readonly ILogger _logger;
public KeyRingProvider(IKeyManager keyManager, KeyManagementOptions keyManagementOptions, IServiceProvider services)
public KeyRingProvider(
IKeyManager keyManager,
IOptions<KeyManagementOptions> keyManagementOptions,
IDefaultKeyResolver defaultKeyResolver,
ILoggerFactory loggerFactory)
: this(
keyManager,
keyManagementOptions,
cacheableKeyRingProvider: null,
defaultKeyResolver: defaultKeyResolver,
loggerFactory: loggerFactory)
{
_keyManagementOptions = new KeyManagementOptions(keyManagementOptions); // clone so new instance is immutable
}
public KeyRingProvider(
IKeyManager keyManager,
IOptions<KeyManagementOptions> keyManagementOptions,
ICacheableKeyRingProvider cacheableKeyRingProvider,
IDefaultKeyResolver defaultKeyResolver,
ILoggerFactory loggerFactory)
{
_keyManagementOptions = new KeyManagementOptions(keyManagementOptions.Value); // clone so new instance is immutable
_keyManager = keyManager;
_cacheableKeyRingProvider = services?.GetService<ICacheableKeyRingProvider>() ?? this;
_logger = services?.GetLogger<KeyRingProvider>();
_defaultKeyResolver = services?.GetService<IDefaultKeyResolver>()
?? new DefaultKeyResolver(_keyManagementOptions.KeyPropagationWindow, _keyManagementOptions.MaxServerClockSkew, services);
_cacheableKeyRingProvider = cacheableKeyRingProvider ?? this;
_defaultKeyResolver = defaultKeyResolver;
_logger = loggerFactory.CreateLogger<KeyRingProvider>();
}
private CacheableKeyRing CreateCacheableKeyRingCore(DateTimeOffset now, IKey keyJustAdded)
@ -46,7 +65,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, defaultKeyPolicy.DefaultKey, allKeys);
}
_logger?.PolicyResolutionStatesThatANewKeyShouldBeAddedToTheKeyRing();
_logger.PolicyResolutionStatesThatANewKeyShouldBeAddedToTheKeyRing();
// We shouldn't call CreateKey more than once, else we risk stack diving. This code path shouldn't
// get hit unless there was an ineligible key with an activation date slightly later than the one we
@ -67,12 +86,12 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
var keyToUse = defaultKeyPolicy.DefaultKey ?? defaultKeyPolicy.FallbackKey;
if (keyToUse == null)
{
_logger?.KeyRingDoesNotContainValidDefaultKey();
_logger.KeyRingDoesNotContainValidDefaultKey();
throw new InvalidOperationException(Resources.KeyRingProvider_NoDefaultKey_AutoGenerateDisabled);
}
else
{
_logger?.UsingFallbackKeyWithExpirationAsDefaultKey(keyToUse.KeyId, keyToUse.ExpirationDate);
_logger.UsingFallbackKeyWithExpirationAsDefaultKey(keyToUse.KeyId, keyToUse.ExpirationDate);
return CreateCacheableKeyRingCoreStep2(now, cacheExpirationToken, keyToUse, allKeys);
}
}
@ -99,9 +118,9 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
Debug.Assert(defaultKey != null);
// Invariant: our caller ensures that CreateEncryptorInstance succeeded at least once
Debug.Assert(defaultKey.CreateEncryptorInstance() != null);
Debug.Assert(CreateEncryptorForKey(defaultKey) != null);
_logger?.UsingKeyAsDefaultKey(defaultKey.KeyId);
_logger.UsingKeyAsDefaultKey(defaultKey.KeyId);
var nextAutoRefreshTime = now + GetRefreshPeriodWithJitter(_keyManagementOptions.KeyRingRefreshPeriod);
@ -116,7 +135,8 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
expirationToken: cacheExpirationToken,
expirationTime: (defaultKey.ExpirationDate <= now) ? nextAutoRefreshTime : Min(defaultKey.ExpirationDate, nextAutoRefreshTime),
defaultKey: defaultKey,
allKeys: allKeys);
allKeys: allKeys,
encryptorFactories: _keyManagementOptions.AuthenticatedEncryptorFactories);
}
public IKeyRing GetCurrentKeyRing()
@ -156,7 +176,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (existingCacheableKeyRing != null)
{
_logger?.ExistingCachedKeyRingIsExpired();
_logger.ExistingCachedKeyRingIsExpired();
}
// It's up to us to refresh the cached keyring.
@ -171,11 +191,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
if (existingCacheableKeyRing != null)
{
_logger?.ErrorOccurredWhileRefreshingKeyRing(ex);
_logger.ErrorOccurredWhileRefreshingKeyRing(ex);
}
else
{
_logger?.ErrorOccurredWhileReadingKeyRing(ex);
_logger.ErrorOccurredWhileReadingKeyRing(ex);
}
// Failures that occur while refreshing the keyring are most likely transient, perhaps due to a
@ -216,6 +236,20 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
}
}
private IAuthenticatedEncryptor CreateEncryptorForKey(IKey key)
{
foreach (var factory in _keyManagementOptions.AuthenticatedEncryptorFactories)
{
var encryptor = factory.CreateEncryptorInstance(key);
if (encryptor != null)
{
return encryptor;
}
}
return null;
}
private static TimeSpan GetRefreshPeriodWithJitter(TimeSpan refreshPeriod)
{
// We'll fudge the refresh period up to -20% so that multiple applications don't try to

View File

@ -4,21 +4,23 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.Cng;
using Microsoft.AspNetCore.DataProtection.Internal;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static System.FormattableString;
using Microsoft.Extensions.Options;
using Microsoft.Win32;
namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
@ -43,9 +45,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
private const string RevokeAllKeysValue = "*";
private readonly IActivator _activator;
private readonly IAuthenticatedEncryptorConfiguration _authenticatedEncryptorConfiguration;
private readonly IInternalXmlKeyManager _internalKeyManager;
private readonly AlgorithmConfiguration _authenticatedEncryptorConfiguration;
private readonly IKeyEscrowSink _keyEscrowSink;
private readonly IInternalXmlKeyManager _internalKeyManager;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger _logger;
private CancellationTokenSource _cacheExpirationTokenSource;
@ -53,59 +56,49 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
/// <summary>
/// Creates an <see cref="XmlKeyManager"/>.
/// </summary>
/// <param name="repository">The repository where keys are stored.</param>
/// <param name="configuration">Configuration for newly-created keys.</param>
/// <param name="services">A provider of optional services.</param>
public XmlKeyManager(
IXmlRepository repository,
IAuthenticatedEncryptorConfiguration configuration,
IServiceProvider services)
/// <param name="keyManagementOptions">The <see cref="IOptions{KeyManagementOptions}"/> instance that provides the configuration.</param>
/// <param name="activator">The <see cref="IActivator"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public XmlKeyManager(IOptions<KeyManagementOptions> keyManagementOptions, IActivator activator, ILoggerFactory loggerFactory)
{
if (repository == null)
{
throw new ArgumentNullException(nameof(repository));
}
_loggerFactory = loggerFactory;
_logger = _loggerFactory.CreateLogger<XmlKeyManager>();
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
KeyEncryptor = services.GetService<IXmlEncryptor>(); // optional
KeyRepository = repository;
_activator = services.GetActivator(); // returns non-null
_authenticatedEncryptorConfiguration = configuration;
_internalKeyManager = services.GetService<IInternalXmlKeyManager>() ?? this;
_keyEscrowSink = services.GetKeyEscrowSink(); // not required
_logger = services.GetLogger<XmlKeyManager>(); // not required
TriggerAndResetCacheExpirationToken(suppressLogging: true);
}
internal XmlKeyManager(IServiceProvider services)
{
// First, see if an explicit encryptor or repository was specified.
// If either was specified, then we won't use the fallback.
KeyEncryptor = services.GetService<IXmlEncryptor>(); // optional
KeyRepository = (KeyEncryptor != null)
? services.GetRequiredService<IXmlRepository>() // required if encryptor is specified
: services.GetService<IXmlRepository>(); // optional if encryptor not specified
// If the repository is missing, then we get both the encryptor and the repository from the fallback.
// If the fallback is missing, the final call to GetRequiredService below will throw.
KeyRepository = keyManagementOptions.Value.XmlRepository;
KeyEncryptor = keyManagementOptions.Value.XmlEncryptor;
if (KeyRepository == null)
{
var defaultKeyServices = services.GetService<IDefaultKeyServices>();
KeyEncryptor = defaultKeyServices?.GetKeyEncryptor(); // optional
KeyRepository = defaultKeyServices?.GetKeyRepository() ?? services.GetRequiredService<IXmlRepository>();
if (KeyEncryptor != null)
{
throw new InvalidOperationException(
Resources.FormatXmlKeyManager_IXmlRepositoryNotFound(nameof(IXmlRepository), nameof(IXmlEncryptor)));
}
else
{
var keyRepositoryEncryptorPair = GetFallbackKeyRepositoryEncryptorPair();
KeyRepository = keyRepositoryEncryptorPair.Key;
KeyEncryptor = keyRepositoryEncryptorPair.Value;
}
}
_activator = services.GetActivator(); // returns non-null
_authenticatedEncryptorConfiguration = services.GetRequiredService<IAuthenticatedEncryptorConfiguration>();
_internalKeyManager = services.GetService<IInternalXmlKeyManager>() ?? this;
_keyEscrowSink = services.GetKeyEscrowSink(); // not required
_logger = services.GetLogger<XmlKeyManager>(); // not required
_authenticatedEncryptorConfiguration = keyManagementOptions.Value.AuthenticatedEncryptorConfiguration;
var escrowSinks = keyManagementOptions.Value.KeyEscrowSinks;
_keyEscrowSink = escrowSinks.Count > 0 ? new AggregateKeyEscrowSink(escrowSinks) : null;
_activator = activator;
TriggerAndResetCacheExpirationToken(suppressLogging: true);
_internalKeyManager = _internalKeyManager ?? this;
}
// Internal for testing.
internal XmlKeyManager(
IOptions<KeyManagementOptions> keyManagementOptions,
IActivator activator,
ILoggerFactory loggerFactory,
IInternalXmlKeyManager internalXmlKeyManager)
: this(keyManagementOptions, activator, loggerFactory)
{
_internalKeyManager = internalXmlKeyManager;
}
internal IXmlEncryptor KeyEncryptor { get; }
@ -177,7 +170,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
else
{
// Skip unknown elements.
_logger?.UnknownElementWithNameFoundInKeyringSkipping(element.Name);
_logger.UnknownElementWithNameFoundInKeyringSkipping(element.Name);
}
}
@ -191,11 +184,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (key != null)
{
key.SetRevoked();
_logger?.MarkedKeyAsRevokedInTheKeyring(revokedKeyId);
_logger.MarkedKeyAsRevokedInTheKeyring(revokedKeyId);
}
else
{
_logger?.TriedToProcessRevocationOfKeyButNoSuchKeyWasFound(revokedKeyId);
_logger.TriedToProcessRevocationOfKeyButNoSuchKeyWasFound(revokedKeyId);
}
}
}
@ -213,7 +206,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
if (key.CreationDate < mostRecentMassRevocationDate)
{
key.SetRevoked();
_logger?.MarkedKeyAsRevokedInTheKeyring(key.KeyId);
_logger.MarkedKeyAsRevokedInTheKeyring(key.KeyId);
}
}
}
@ -239,14 +232,14 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
DateTimeOffset activationDate = (DateTimeOffset)keyElement.Element(ActivationDateElementName);
DateTimeOffset expirationDate = (DateTimeOffset)keyElement.Element(ExpirationDateElementName);
_logger?.FoundKey(keyId);
_logger.FoundKey(keyId);
return new DeferredKey(
keyId: keyId,
creationDate: creationDate,
activationDate: activationDate,
expirationDate: expirationDate,
keyManager: _internalKeyManager,
keyManager: this,
keyElement: keyElement);
}
catch (Exception ex)
@ -270,14 +263,14 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
// this is a mass revocation of all keys as of the specified revocation date
DateTimeOffset massRevocationDate = (DateTimeOffset)revocationElement.Element(RevocationDateElementName);
_logger?.FoundRevocationOfAllKeysCreatedPriorTo(massRevocationDate);
_logger.FoundRevocationOfAllKeysCreatedPriorTo(massRevocationDate);
return massRevocationDate;
}
else
{
// only one key is being revoked
var keyId = XmlConvert.ToGuid(keyIdAsString);
_logger?.FoundRevocationOfKey(keyId);
_logger.FoundRevocationOfKey(keyId);
return keyId;
}
}
@ -285,7 +278,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
// Any exceptions that occur are fatal - we don't want to continue if we cannot process
// revocation information.
_logger?.ExceptionWhileProcessingRevocationElement(revocationElement, ex);
_logger.ExceptionWhileProcessingRevocationElement(revocationElement, ex);
throw;
}
}
@ -299,7 +292,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// <reason>...</reason>
// </revocation>
_logger?.RevokingAllKeysAsOfForReason(revocationDate, reason);
_logger.RevokingAllKeysAsOfForReason(revocationDate, reason);
var revocationElement = new XElement(RevocationElementName,
new XAttribute(VersionAttributeName, 1),
@ -327,7 +320,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
{
if (!suppressLogging)
{
_logger?.KeyCacheExpirationTokenTriggeredByOperation(opName);
_logger.KeyCacheExpirationTokenTriggeredByOperation(opName);
}
Interlocked.Exchange(ref _cacheExpirationTokenSource, new CancellationTokenSource())?.Cancel();
@ -341,10 +334,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// include sensitive information in the exception message.
// write sanitized <key> element
_logger?.ExceptionWhileProcessingKeyElement(keyElement.WithoutChildNodes(), error);
_logger.ExceptionWhileProcessingKeyElement(keyElement.WithoutChildNodes(), error);
// write full <key> element
_logger?.AnExceptionOccurredWhileProcessingElementDebug(keyElement, error);
_logger.AnExceptionOccurredWhileProcessingElementDebug(keyElement, error);
}
@ -359,13 +352,13 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// </descriptor>
// </key>
_logger?.CreatingKey(keyId, creationDate, activationDate, expirationDate);
_logger.CreatingKey(keyId, creationDate, activationDate, expirationDate);
var newDescriptor = _authenticatedEncryptorConfiguration.CreateNewDescriptor()
?? CryptoUtil.Fail<IAuthenticatedEncryptorDescriptor>("CreateNewDescriptor returned null.");
var descriptorXmlInfo = newDescriptor.ExportToXml();
_logger?.DescriptorDeserializerTypeForKeyIs(keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName);
_logger.DescriptorDeserializerTypeForKeyIs(keyId, descriptorXmlInfo.DeserializerType.AssemblyQualifiedName);
// build the <key> element
var keyElement = new XElement(KeyElementName,
@ -381,23 +374,23 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// If key escrow policy is in effect, write the *unencrypted* key now.
if (_keyEscrowSink != null)
{
_logger?.KeyEscrowSinkFoundWritingKeyToEscrow(keyId);
_logger.KeyEscrowSinkFoundWritingKeyToEscrow(keyId);
}
else
{
_logger?.NoKeyEscrowSinkFoundNotWritingKeyToEscrow(keyId);
_logger.NoKeyEscrowSinkFoundNotWritingKeyToEscrow(keyId);
}
_keyEscrowSink?.Store(keyId, keyElement);
// If an XML encryptor has been configured, protect secret key material now.
if (KeyEncryptor == null)
{
_logger?.NoXMLEncryptorConfiguredKeyMayBePersistedToStorageInUnencryptedForm(keyId);
_logger.NoXMLEncryptorConfiguredKeyMayBePersistedToStorageInUnencryptedForm(keyId);
}
var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement;
// Persist it to the underlying repository and trigger the cancellation token.
var friendlyName = Invariant($"key-{keyId:D}");
var friendlyName = string.Format(CultureInfo.InvariantCulture, "key-{0:D}", keyId);
KeyRepository.StoreElement(possiblyEncryptedKeyElement, friendlyName);
TriggerAndResetCacheExpirationToken();
@ -440,7 +433,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// <reason>...</reason>
// </revocation>
_logger?.RevokingKeyForReason(keyId, revocationDate, reason);
_logger.RevokingKeyForReason(keyId, revocationDate, reason);
var revocationElement = new XElement(RevocationElementName,
new XAttribute(VersionAttributeName, 1),
@ -450,9 +443,97 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
new XElement(ReasonElementName, reason));
// Persist it to the underlying repository and trigger the cancellation token
var friendlyName = Invariant($"revocation-{keyId:D}");
var friendlyName = string.Format(CultureInfo.InvariantCulture, "revocation-{0:D}", keyId);
KeyRepository.StoreElement(revocationElement, friendlyName);
TriggerAndResetCacheExpirationToken();
}
internal KeyValuePair<IXmlRepository, IXmlEncryptor> GetFallbackKeyRepositoryEncryptorPair()
{
IXmlRepository repository = null;
IXmlEncryptor encryptor = null;
// If we're running in Azure Web Sites, the key repository goes in the %HOME% directory.
var azureWebSitesKeysFolder = FileSystemXmlRepository.GetKeyStorageDirectoryForAzureWebSites();
if (azureWebSitesKeysFolder != null)
{
_logger.UsingAzureAsKeyRepository(azureWebSitesKeysFolder.FullName);
// Cloud DPAPI isn't yet available, so we don't encrypt keys at rest.
// This isn't all that different than what Azure Web Sites does today, and we can always add this later.
repository = new FileSystemXmlRepository(azureWebSitesKeysFolder, _loggerFactory);
}
else
{
// If the user profile is available, store keys in the user profile directory.
var localAppDataKeysFolder = FileSystemXmlRepository.DefaultKeyStorageDirectory;
if (localAppDataKeysFolder != null)
{
if (OSVersionUtil.IsWindows())
{
// If the user profile is available, we can protect using DPAPI.
// Probe to see if protecting to local user is available, and use it as the default if so.
encryptor = new DpapiXmlEncryptor(
protectToLocalMachine: !DpapiSecretSerializerHelper.CanProtectToCurrentUserAccount(),
loggerFactory: _loggerFactory);
}
repository = new FileSystemXmlRepository(localAppDataKeysFolder, _loggerFactory);
if (encryptor != null)
{
_logger.UsingProfileAsKeyRepositoryWithDPAPI(localAppDataKeysFolder.FullName);
}
else
{
_logger.UsingProfileAsKeyRepository(localAppDataKeysFolder.FullName);
}
}
else
{
// Use profile isn't available - can we use the HKLM registry?
RegistryKey regKeyStorageKey = null;
if (OSVersionUtil.IsWindows())
{
regKeyStorageKey = RegistryXmlRepository.DefaultRegistryKey;
}
if (regKeyStorageKey != null)
{
// If the user profile isn't available, we can protect using DPAPI (to machine).
encryptor = new DpapiXmlEncryptor(protectToLocalMachine: true, loggerFactory: _loggerFactory);
repository = new RegistryXmlRepository(regKeyStorageKey, _loggerFactory);
_logger.UsingRegistryAsKeyRepositoryWithDPAPI(regKeyStorageKey.Name);
}
else
{
// Final fallback - use an ephemeral repository since we don't know where else to go.
// This can only be used for development scenarios.
repository = new EphemeralXmlRepository(_loggerFactory);
_logger.UsingEphemeralKeyRepository();
}
}
}
return new KeyValuePair<IXmlRepository, IXmlEncryptor>(repository, encryptor);
}
private sealed class AggregateKeyEscrowSink : IKeyEscrowSink
{
private readonly IList<IKeyEscrowSink> _sinks;
public AggregateKeyEscrowSink(IList<IKeyEscrowSink> sinks)
{
_sinks = sinks;
}
public void Store(Guid keyId, XElement element)
{
foreach (var sink in _sinks)
{
sink.Store(keyId, element);
}
}
}
}
}

View File

@ -394,6 +394,22 @@ namespace Microsoft.AspNetCore.DataProtection
return string.Format(CultureInfo.CurrentCulture, GetString("LifetimeMustNotBeNegative"), p0);
}
/// <summary>
/// The '{0}' instance could not be found. When an '{1}' instance is set, a corresponding '{0}' instance must also be set.
/// </summary>
internal static string XmlKeyManager_IXmlRepositoryNotFound
{
get { return GetString("XmlKeyManager_IXmlRepositoryNotFound"); }
}
/// <summary>
/// The '{0}' instance could not be found. When an '{1}' instance is set, a corresponding '{0}' instance must also be set.
/// </summary>
internal static string FormatXmlKeyManager_IXmlRepositoryNotFound(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("XmlKeyManager_IXmlRepositoryNotFound"), p0, p1);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -12,13 +12,13 @@ namespace Microsoft.AspNetCore.DataProtection
private const string To = "Microsoft.AspNetCore.DataProtection";
private readonly ILogger _logger;
public RC1ForwardingActivator(IServiceProvider services) : this(services, null)
public RC1ForwardingActivator(IServiceProvider services) : this(services, DataProtectionProviderFactory.GetDefaultLoggerFactory())
{
}
public RC1ForwardingActivator(IServiceProvider services, ILoggerFactory loggerFactory) : base(services)
{
_logger = loggerFactory?.CreateLogger(typeof(RC1ForwardingActivator));
_logger = loggerFactory.CreateLogger(typeof(RC1ForwardingActivator));
}
public override object CreateInstance(Type expectedBaseType, string implementationTypeName)
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.DataProtection
var type = Type.GetType(forwardedImplementationTypeName, false);
if (type != null)
{
_logger?.LogDebug("Forwarded activator type request from {FromType} to {ToType}",
_logger.LogDebug("Forwarded activator type request from {FromType} to {ToType}",
implementationTypeName,
forwardedImplementationTypeName);

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

View File

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

View File

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

View File

@ -3,14 +3,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using static System.FormattableString;
namespace Microsoft.AspNetCore.DataProtection.Repositories
{
/// <summary>
@ -26,21 +25,8 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
/// Creates a <see cref="RegistryXmlRepository"/> with keys stored in the given registry key.
/// </summary>
/// <param name="registryKey">The registry key in which to persist key material.</param>
public RegistryXmlRepository(RegistryKey registryKey)
: this(registryKey, services: null)
{
if (registryKey == null)
{
throw new ArgumentNullException(nameof(registryKey));
}
}
/// <summary>
/// Creates a <see cref="RegistryXmlRepository"/> with keys stored in the given registry key.
/// </summary>
/// <param name="registryKey">The registry key in which to persist key material.</param>
/// <param name="services">The <see cref="IServiceProvider"/> used to resolve services.</param>
public RegistryXmlRepository(RegistryKey registryKey, IServiceProvider services)
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public RegistryXmlRepository(RegistryKey registryKey, ILoggerFactory loggerFactory)
{
if (registryKey == null)
{
@ -48,8 +34,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
}
RegistryKey = registryKey;
Services = services;
_logger = services?.GetLogger<RegistryXmlRepository>();
_logger = loggerFactory.CreateLogger<RegistryXmlRepository>();
}
/// <summary>
@ -67,11 +52,6 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
/// </summary>
public RegistryKey RegistryKey { get; }
/// <summary>
/// The <see cref="IServiceProvider"/> provided to the constructor.
/// </summary>
protected IServiceProvider Services { get; }
public virtual IReadOnlyCollection<XElement> GetAllElements()
{
// forces complete enumeration
@ -107,7 +87,11 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
// Even though this is in HKLM, WAS ensures that applications hosted in IIS are properly isolated.
// See APP_POOL::EnsureSharedMachineKeyStorage in WAS source for more info.
// The version number will need to change if IIS hosts Core CLR directly.
var aspnetAutoGenKeysBaseKeyName = Invariant($@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{WindowsIdentity.GetCurrent().User.Value}");
var aspnetAutoGenKeysBaseKeyName = string.Format(
CultureInfo.InvariantCulture,
@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{0}",
WindowsIdentity.GetCurrent().User.Value);
var aspnetBaseKey = hklmBaseKey.OpenSubKey(aspnetAutoGenKeysBaseKeyName, writable: true);
if (aspnetBaseKey != null)
{
@ -141,7 +125,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
private XElement ReadElementFromRegKey(RegistryKey regKey, string valueName)
{
_logger?.ReadingDataFromRegistryKeyValue(regKey, valueName);
_logger.ReadingDataFromRegistryKeyValue(regKey, valueName);
var data = regKey.GetValue(valueName) as string;
return (!String.IsNullOrEmpty(data)) ? XElement.Parse(data) : null;
@ -157,7 +141,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
if (!IsSafeRegistryValueName(friendlyName))
{
var newFriendlyName = Guid.NewGuid().ToString();
_logger?.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
_logger.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
friendlyName = newFriendlyName;
}

View File

@ -189,4 +189,7 @@
<data name="LifetimeMustNotBeNegative" xml:space="preserve">
<value>{0} must not be negative</value>
</data>
<data name="XmlKeyManager_IXmlRepositoryNotFound" xml:space="preserve">
<value>The '{0}' instance could not be found. When an '{1}' instance is set, a corresponding '{0}' instance must also be set.</value>
</data>
</root>

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

View File

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

View File

@ -21,28 +21,18 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
private readonly ILogger _logger;
private readonly bool _protectToLocalMachine;
/// <summary>
/// Creates a <see cref="DpapiXmlEncryptor"/> given a protection scope.
/// </summary>
/// <param name="protectToLocalMachine">'true' if the data should be decipherable by anybody on the local machine,
/// 'false' if the data should only be decipherable by the current Windows user account.</param>
public DpapiXmlEncryptor(bool protectToLocalMachine)
: this(protectToLocalMachine, services: null)
{
}
/// <summary>
/// Creates a <see cref="DpapiXmlEncryptor"/> given a protection scope and an <see cref="IServiceProvider"/>.
/// </summary>
/// <param name="protectToLocalMachine">'true' if the data should be decipherable by anybody on the local machine,
/// 'false' if the data should only be decipherable by the current Windows user account.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to provide ancillary services.</param>
public DpapiXmlEncryptor(bool protectToLocalMachine, IServiceProvider services)
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public DpapiXmlEncryptor(bool protectToLocalMachine, ILoggerFactory loggerFactory)
{
CryptoUtil.AssertPlatformIsWindows();
_protectToLocalMachine = protectToLocalMachine;
_logger = services.GetLogger<DpapiXmlEncryptor>();
_logger = loggerFactory.CreateLogger<DpapiXmlEncryptor>();
}
/// <summary>
@ -62,11 +52,11 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
}
if (_protectToLocalMachine)
{
_logger?.EncryptingToWindowsDPAPIForLocalMachineAccount();
_logger.EncryptingToWindowsDPAPIForLocalMachineAccount();
}
else
{
_logger?.EncryptingToWindowsDPAPIForCurrentUserAccount(WindowsIdentity.GetCurrent().Name);
_logger.EncryptingToWindowsDPAPIForCurrentUserAccount(WindowsIdentity.GetCurrent().Name);
}
// Convert the XML element to a binary secret so that it can be run through DPAPI
@ -80,7 +70,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
}
catch (Exception ex)
{
_logger?.ErrorOccurredWhileEncryptingToWindowsDPAPI(ex);
_logger.ErrorOccurredWhileEncryptingToWindowsDPAPI(ex);
throw;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,4 +21,8 @@
<PackageReference Include="xunit" Version="2.2.0-*" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</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.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -13,13 +15,14 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_Cbc_CreatesAppropriateDescriptor()
{
// Arrange
var control = new AuthenticatedEncryptorDescriptor(
new AuthenticatedEncryptionSettings()
var descriptor = new AuthenticatedEncryptorDescriptor(
new AuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_192_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA512
},
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
const string xml = @"
<encryptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
@ -27,7 +30,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
<validation algorithm='HMACSHA512' />
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
</encryptor>";
var test = new AuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
var deserializedDescriptor = new AuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as AuthenticatedEncryptorDescriptor);
// Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -36,5 +40,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext);
}
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(AuthenticatedEncryptorDescriptor descriptor)
{
var key = new Key(
Guid.NewGuid(),
DateTimeOffset.Now,
DateTimeOffset.Now + TimeSpan.FromHours(1),
DateTimeOffset.Now + TimeSpan.FromDays(30),
descriptor);
var encryptorFactory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
return encryptorFactory.CreateEncryptorInstance(key);
}
}
}

View File

@ -8,9 +8,11 @@ using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Cryptography.Cng;
using Microsoft.AspNetCore.Cryptography.SafeHandles;
using Microsoft.AspNetCore.DataProtection.Cng;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Managed;
using Microsoft.AspNetCore.DataProtection.Test.Shared;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -38,7 +40,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8),
hmacAlgorithmHandle: BCryptAlgorithmHandle.OpenAlgorithmHandle(hashAlgorithm, hmac: true));
var test = CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey).CreateEncryptorInstance();
var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey));
// Act & assert - data round trips properly from control to test
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -64,7 +66,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
keyDerivationKey: masterKey,
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_GCM,
symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8));
var test = CreateDescriptor(encryptionAlgorithm, ValidationAlgorithm.HMACSHA256 /* unused */, masterKey).CreateEncryptorInstance();
var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, ValidationAlgorithm.HMACSHA256 /* unused */, masterKey));
// Act & assert - data round trips properly from control to test
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -102,7 +104,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
symmetricAlgorithmFactory: () => Aes.Create(),
symmetricAlgorithmKeySizeInBytes: keyLengthInBits / 8,
validationAlgorithmFactory: validationAlgorithmFactory);
var test = CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey).CreateEncryptorInstance();
var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, validationAlgorithm, masterKey));
// Act & assert - data round trips properly from control to test
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -160,11 +162,26 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
private static AuthenticatedEncryptorDescriptor CreateDescriptor(EncryptionAlgorithm encryptionAlgorithm, ValidationAlgorithm validationAlgorithm, ISecret masterKey)
{
return new AuthenticatedEncryptorDescriptor(new AuthenticatedEncryptionSettings()
return new AuthenticatedEncryptorDescriptor(new AuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = encryptionAlgorithm,
ValidationAlgorithm = validationAlgorithm
}, masterKey);
}
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(AuthenticatedEncryptorDescriptor descriptor)
{
// Dummy key with the specified descriptor.
var key = new Key(
Guid.NewGuid(),
DateTimeOffset.Now,
DateTimeOffset.Now + TimeSpan.FromHours(1),
DateTimeOffset.Now + TimeSpan.FromDays(30),
descriptor);
var encryptorFactory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
return encryptorFactory.CreateEncryptorInstance(key);
}
}
}

View File

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

View File

@ -4,8 +4,10 @@
using System;
using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Test.Shared;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -17,8 +19,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_CreatesAppropriateDescriptor()
{
// Arrange
var control = new CngCbcAuthenticatedEncryptorDescriptor(
new CngCbcAuthenticatedEncryptionSettings()
var descriptor = new CngCbcAuthenticatedEncryptorDescriptor(
new CngCbcAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM,
EncryptionAlgorithmKeySize = 192,
@ -26,7 +28,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
HashAlgorithm = Constants.BCRYPT_SHA512_ALGORITHM,
HashAlgorithmProvider = null
},
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
const string xml = @"
<descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
@ -34,7 +37,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
<hash algorithm='SHA512' />
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
</descriptor>";
var test = new CngCbcAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
var deserializedDescriptor = new CngCbcAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as CngCbcAuthenticatedEncryptorDescriptor);
// Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -43,5 +47,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext);
}
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(CngCbcAuthenticatedEncryptorDescriptor descriptor)
{
var key = new Key(
Guid.NewGuid(),
DateTimeOffset.Now,
DateTimeOffset.Now + TimeSpan.FromHours(1),
DateTimeOffset.Now + TimeSpan.FromDays(30),
descriptor);
var encryptorFactory = new CngCbcAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
return encryptorFactory.CreateEncryptorInstance(key);
}
}
}

View File

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

View File

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

View File

@ -4,8 +4,10 @@
using System;
using System.Xml.Linq;
using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.Test.Shared;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -17,21 +19,23 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_CreatesAppropriateDescriptor()
{
// Arrange
var control = new CngGcmAuthenticatedEncryptorDescriptor(
new CngGcmAuthenticatedEncryptionSettings()
var descriptor = new CngGcmAuthenticatedEncryptorDescriptor(
new CngGcmAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithm = Constants.BCRYPT_AES_ALGORITHM,
EncryptionAlgorithmKeySize = 192,
EncryptionAlgorithmProvider = null
},
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
const string xml = @"
<descriptor version='1' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
<encryption algorithm='AES' keyLength='192' />
<masterKey enc:requiresEncryption='true'>k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==</masterKey>
</descriptor>";
var test = new CngGcmAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
var deserializedDescriptor = new CngGcmAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as CngGcmAuthenticatedEncryptorDescriptor);
// Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -40,5 +44,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext);
}
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(CngGcmAuthenticatedEncryptorDescriptor descriptor)
{
var key = new Key(
keyId: Guid.NewGuid(),
creationDate: DateTimeOffset.Now,
activationDate: DateTimeOffset.Now + TimeSpan.FromHours(1),
expirationDate: DateTimeOffset.Now + TimeSpan.FromDays(30),
descriptor: descriptor);
var encryptorFactory = new CngGcmAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
return encryptorFactory.CreateEncryptorInstance(key);
}
}
}

View File

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

View File

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

View File

@ -4,6 +4,8 @@
using System;
using System.Security.Cryptography;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel
@ -18,16 +20,17 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_BuiltInTypes_CreatesAppropriateDescriptor(Type encryptionAlgorithmType, Type validationAlgorithmType)
{
// Arrange
var control = new ManagedAuthenticatedEncryptorDescriptor(
new ManagedAuthenticatedEncryptionSettings()
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
new ManagedAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithmType = encryptionAlgorithmType,
EncryptionAlgorithmKeySize = 192,
ValidationAlgorithmType = validationAlgorithmType
},
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
string xml = String.Format(@"
string xml = string.Format(@"
<descriptor>
<encryption algorithm='{0}' keyLength='192' />
<validation algorithm='{1}' />
@ -36,7 +39,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
</masterKey>
</descriptor>",
encryptionAlgorithmType.Name, validationAlgorithmType.Name);
var test = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as ManagedAuthenticatedEncryptorDescriptor);
// Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -50,16 +54,17 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
public void ImportFromXml_CustomType_CreatesAppropriateDescriptor()
{
// Arrange
var control = new ManagedAuthenticatedEncryptorDescriptor(
new ManagedAuthenticatedEncryptionSettings()
var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
new ManagedAuthenticatedEncryptorConfiguration()
{
EncryptionAlgorithmType = typeof(Aes),
EncryptionAlgorithmKeySize = 192,
ValidationAlgorithmType = typeof(HMACSHA384)
},
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret()).CreateEncryptorInstance();
"k88VrwGLINfVAqzlAp7U4EAjdlmUG17c756McQGdjHU8Ajkfc/A3YOKdqlMcF6dXaIxATED+g2f62wkRRRRRzA==".ToSecret());
var control = CreateEncryptorInstanceFromDescriptor(descriptor);
string xml = String.Format(@"
string xml = string.Format(@"
<descriptor>
<encryption algorithm='{0}' keyLength='192' />
<validation algorithm='{1}' />
@ -68,7 +73,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
</masterKey>
</descriptor>",
typeof(Aes).AssemblyQualifiedName, typeof(HMACSHA384).AssemblyQualifiedName);
var test = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)).CreateEncryptorInstance();
var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as ManagedAuthenticatedEncryptorDescriptor);
// Act & assert
byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
@ -77,5 +83,19 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
Assert.Equal(plaintext, roundTripPlaintext);
}
private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(ManagedAuthenticatedEncryptorDescriptor descriptor)
{
var key = new Key(
Guid.NewGuid(),
DateTimeOffset.Now,
DateTimeOffset.Now + TimeSpan.FromHours(1),
DateTimeOffset.Now + TimeSpan.FromDays(30),
descriptor);
var encryptorFactory = new ManagedAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
return encryptorFactory.CreateEncryptorInstance(key);
}
}
}

View File

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

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

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