Significant refactorings throughout the data protection stack
- Move IDataProtectionProvider, IDataProtector, and extension methods to their own package - Simplify the APIs for registering and configuring the system - Default implementation now auto-detects capabilities of OS - Use EncryptedXml for X.509 certificate-based encryption - Add ability to escrow secret material upon key creation - Use centralized system policy for default algorithm selection - Simplify System.Web compatibility layer - Add unit tests, logging, and doc comments throughout solution
This commit is contained in:
parent
04008af479
commit
e8cc1106d8
|
|
@ -9,8 +9,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtec
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Azure", "src\Microsoft.AspNet.DataProtection.Azure\Microsoft.AspNet.DataProtection.Azure.kproj", "{DF3671D7-A9B1-45F1-A195-0AD596001735}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Compatibility", "src\Microsoft.AspNet.DataProtection.Compatibility\Microsoft.AspNet.DataProtection.Compatibility.kproj", "{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{60336AB3-948D-4D15-A5FB-F32A2B91E814}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Test", "test\Microsoft.AspNet.DataProtection.Test\Microsoft.AspNet.DataProtection.Test.kproj", "{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}"
|
||||
|
|
@ -23,6 +21,16 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Cryptograp
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Cryptography.Internal.Test", "test\Microsoft.AspNet.Cryptography.Internal.Test\Microsoft.AspNet.Cryptography.Internal.Test.kproj", "{37053D5F-5B61-47CE-8B72-298CE007FFB0}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Interfaces", "src\Microsoft.AspNet.DataProtection.Interfaces\Microsoft.AspNet.DataProtection.Interfaces.kproj", "{4B115BDE-B253-46A6-97BF-A8B37B344FF2}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Interfaces.Test", "test\Microsoft.AspNet.DataProtection.Interfaces.Test\Microsoft.AspNet.DataProtection.Interfaces.Test.kproj", "{FF650A69-DEE4-4B36-9E30-264EE7CFB478}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Test.Shared", "test\Microsoft.AspNet.DataProtection.Test.Shared\Microsoft.AspNet.DataProtection.Test.Shared.kproj", "{4F14BA2A-4F04-4676-8586-EC380977EE2E}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.Shared", "src\Microsoft.AspNet.DataProtection.Shared\Microsoft.AspNet.DataProtection.Shared.kproj", "{3277BB22-033F-4010-8131-A515B910CAAD}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.DataProtection.SystemWeb", "src\Microsoft.AspNet.DataProtection.SystemWeb\Microsoft.AspNet.DataProtection.SystemWeb.kproj", "{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -43,12 +51,6 @@ Global
|
|||
{DF3671D7-A9B1-45F1-A195-0AD596001735}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DF3671D7-A9B1-45F1-A195-0AD596001735}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DF3671D7-A9B1-45F1-A195-0AD596001735}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -87,6 +89,46 @@ Global
|
|||
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{37053D5F-5B61-47CE-8B72-298CE007FFB0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -94,11 +136,15 @@ Global
|
|||
GlobalSection(NestedProjects) = preSolution
|
||||
{1E570CD4-6F12-44F4-961E-005EE2002BC2} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{DF3671D7-A9B1-45F1-A195-0AD596001735} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{C2FD9D02-AA0E-45FA-8561-EE357A94B73D} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{7A637185-2BA1-437D-9D4C-7CC4F94CF7BF} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
|
||||
{E2779976-A28C-4365-A4BB-4AD854FAF23E} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{421F0383-34B1-402D-807B-A94542513ABA} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{42C97F52-8D56-46BD-A712-4F22BED157A7} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
|
||||
{37053D5F-5B61-47CE-8B72-298CE007FFB0} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
|
||||
{4B115BDE-B253-46A6-97BF-A8B37B344FF2} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{FF650A69-DEE4-4B36-9E30-264EE7CFB478} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
|
||||
{4F14BA2A-4F04-4676-8586-EC380977EE2E} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
|
||||
{3277BB22-033F-4010-8131-A515B910CAAD} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
{E3552DEB-4173-43AE-BF69-3C10DFF3BAB6} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use assembly='WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
|
||||
use namespace='System.IO.Packaging'
|
||||
|
||||
var VERSION='0.1'
|
||||
var FULL_VERSION='0.1'
|
||||
|
|
@ -5,3 +7,39 @@ var AUTHORS='Microsoft Open Technologies, Inc.'
|
|||
|
||||
use-standard-lifecycle
|
||||
k-standard-goals
|
||||
|
||||
#nupkg-patch target='compile'
|
||||
@{
|
||||
var packagePaths = Files.Include("artifacts/build/**/Microsoft.AspNet.DataProtection.SystemWeb.*.nupkg")
|
||||
.Exclude("**/*.symbols.nupkg");
|
||||
foreach (var packagePath in packagePaths)
|
||||
{
|
||||
using (var package = Package.Open(packagePath, FileMode.Open, FileAccess.ReadWrite))
|
||||
{
|
||||
CreatePartFromFile(
|
||||
package,
|
||||
@"src\Microsoft.AspNet.DataProtection.SystemWeb\web.config.transform",
|
||||
@"content\web.config.transform");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
functions
|
||||
@{
|
||||
PackagePart CreatePartFromFile(
|
||||
Package destination,
|
||||
string sourceFileName,
|
||||
string partUriString)
|
||||
{
|
||||
var partUri = PackUriHelper.CreatePartUri(new Uri(partUriString, UriKind.Relative));
|
||||
var packagePart = destination.CreatePart(partUri, "application/octet", CompressionOption.Maximum);
|
||||
|
||||
using (var sourceStream = File.OpenRead(sourceFileName))
|
||||
using (var stream = packagePart.GetStream())
|
||||
{
|
||||
sourceStream.CopyTo(stream);
|
||||
}
|
||||
|
||||
return packagePart;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
using Microsoft.AspNet.Cryptography.Internal;
|
||||
|
||||
|
|
@ -14,16 +13,16 @@ namespace Microsoft.AspNet.Cryptography.Cng
|
|||
{
|
||||
// MSDN says these fields represent the key length in bytes.
|
||||
// It's wrong: these key lengths are all actually in bits.
|
||||
private uint dwMinLength;
|
||||
private uint dwMaxLength;
|
||||
private uint dwIncrement;
|
||||
internal uint dwMinLength;
|
||||
internal uint dwMaxLength;
|
||||
internal uint dwIncrement;
|
||||
|
||||
public void EnsureValidKeyLength(uint keyLengthInBits)
|
||||
{
|
||||
if (!IsValidKeyLength(keyLengthInBits))
|
||||
{
|
||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength, keyLengthInBits, dwMinLength, dwMaxLength, dwIncrement);
|
||||
throw new ArgumentException(message, "keyLengthInBits");
|
||||
string message = Resources.FormatBCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength(keyLengthInBits, dwMinLength, dwMaxLength, dwIncrement);
|
||||
throw new ArgumentOutOfRangeException(nameof(keyLengthInBits), message);
|
||||
}
|
||||
CryptoUtil.Assert(keyLengthInBits % 8 == 0, "keyLengthInBits % 8 == 0");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,14 @@ using System;
|
|||
|
||||
namespace Microsoft.AspNet.Cryptography.Cng
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps utility BCRYPT APIs that don't work directly with handles.
|
||||
/// </summary>
|
||||
internal unsafe static class BCryptUtil
|
||||
{
|
||||
// helper function that's similar to RNGCryptoServiceProvider, but works directly with pointers
|
||||
/// <summary>
|
||||
/// Fills a buffer with cryptographically secure random data.
|
||||
/// </summary>
|
||||
public static void GenRandom(byte* pbBuffer, uint cbBuffer)
|
||||
{
|
||||
if (cbBuffer != 0)
|
||||
|
|
|
|||
|
|
@ -23,86 +23,27 @@ namespace Microsoft.AspNet.Cryptography.Cng
|
|||
private static CachedAlgorithmInfo _sha512 = new CachedAlgorithmInfo(() => GetHashAlgorithm(algorithm: Constants.BCRYPT_SHA512_ALGORITHM));
|
||||
private static CachedAlgorithmInfo _sp800_108_ctr_hmac = new CachedAlgorithmInfo(GetSP800_108_CTR_HMACAlgorithm);
|
||||
|
||||
public static BCryptAlgorithmHandle AES_CBC
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _aesCbc);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle AES_CBC => CachedAlgorithmInfo.GetAlgorithmHandle(ref _aesCbc);
|
||||
|
||||
public static BCryptAlgorithmHandle AES_GCM
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _aesGcm);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle AES_GCM => CachedAlgorithmInfo.GetAlgorithmHandle(ref _aesGcm);
|
||||
|
||||
public static BCryptAlgorithmHandle HMAC_SHA1
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha1);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle HMAC_SHA1 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha1);
|
||||
|
||||
public static BCryptAlgorithmHandle HMAC_SHA256
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha256);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle HMAC_SHA256 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha256);
|
||||
|
||||
public static BCryptAlgorithmHandle HMAC_SHA512
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha512);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle HMAC_SHA512 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _hmacSha512);
|
||||
|
||||
// Only available on Win8+.
|
||||
public static BCryptAlgorithmHandle PBKDF2
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _pbkdf2);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle PBKDF2 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _pbkdf2);
|
||||
|
||||
public static BCryptAlgorithmHandle SHA1
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha1);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle SHA1 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha1);
|
||||
|
||||
public static BCryptAlgorithmHandle SHA256
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha256);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle SHA256 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha256);
|
||||
|
||||
public static BCryptAlgorithmHandle SHA512
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha512);
|
||||
}
|
||||
}
|
||||
public static BCryptAlgorithmHandle SHA512 => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sha512);
|
||||
|
||||
public static BCryptAlgorithmHandle SP800_108_CTR_HMAC
|
||||
{
|
||||
get
|
||||
{
|
||||
return CachedAlgorithmInfo.GetAlgorithmHandle(ref _sp800_108_ctr_hmac);
|
||||
}
|
||||
}
|
||||
// Only available on Win8+.
|
||||
public static BCryptAlgorithmHandle SP800_108_CTR_HMAC => CachedAlgorithmInfo.GetAlgorithmHandle(ref _sp800_108_ctr_hmac);
|
||||
|
||||
private static BCryptAlgorithmHandle GetAesAlgorithm(string chainingMode)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ namespace Microsoft.AspNet.Cryptography.Cng
|
|||
}
|
||||
}
|
||||
|
||||
public static bool IsBCryptOnWin7OrLaterAvailable()
|
||||
public static bool IsWindows()
|
||||
{
|
||||
return (_osVersion >= OSVersion.Win7OrLater);
|
||||
}
|
||||
|
||||
public static bool IsBCryptOnWin8OrLaterAvailable()
|
||||
public static bool IsWindows8OrLater()
|
||||
{
|
||||
return (_osVersion >= OSVersion.Win8OrLater);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ using System.Diagnostics;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.Cryptography.Internal;
|
||||
|
||||
#if !DNXCORE50
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
|
|
@ -32,6 +34,24 @@ namespace Microsoft.AspNet.Cryptography
|
|||
Assert(safeHandle != null && !safeHandle.IsInvalid, "Safe handle is invalid.");
|
||||
}
|
||||
|
||||
// Asserts that the current platform is Windows; throws PlatformNotSupportedException otherwise.
|
||||
public static void AssertPlatformIsWindows()
|
||||
{
|
||||
if (!OSVersionUtil.IsWindows())
|
||||
{
|
||||
throw new PlatformNotSupportedException(Resources.Platform_Windows7Required);
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that the current platform is Windows 8 or above; throws PlatformNotSupportedException otherwise.
|
||||
public static void AssertPlatformIsWindows8OrLater()
|
||||
{
|
||||
if (!OSVersionUtil.IsWindows8OrLater())
|
||||
{
|
||||
throw new PlatformNotSupportedException(Resources.Platform_Windows8Required);
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't a typical Debug.Fail; an error always occurs, even in retail builds.
|
||||
// This method doesn't return, but since the CLR doesn't allow specifying a 'never'
|
||||
// return type, we mimic it by specifying our return type as Exception. That way
|
||||
|
|
|
|||
|
|
@ -12,4 +12,5 @@ using System.Runtime.InteropServices;
|
|||
[assembly: InternalsVisibleTo("Microsoft.AspNet.Cryptography.KeyDerivation")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.Cryptography.KeyDerivation.Test")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.DataProtection")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.DataProtection.Interfaces.Test")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.DataProtection.Test")]
|
||||
|
|
|
|||
|
|
@ -42,6 +42,38 @@ namespace Microsoft.AspNet.Cryptography.Internal
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This operation requires Windows 7 / Windows Server 2008 R2 or later.
|
||||
/// </summary>
|
||||
internal static string Platform_Windows7Required
|
||||
{
|
||||
get { return GetString("Platform_Windows7Required"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This operation requires Windows 7 / Windows Server 2008 R2 or later.
|
||||
/// </summary>
|
||||
internal static string FormatPlatform_Windows7Required()
|
||||
{
|
||||
return GetString("Platform_Windows7Required");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This operation requires Windows 8 / Windows Server 2012 or later.
|
||||
/// </summary>
|
||||
internal static string Platform_Windows8Required
|
||||
{
|
||||
get { return GetString("Platform_Windows8Required"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This operation requires Windows 8 / Windows Server 2012 or later.
|
||||
/// </summary>
|
||||
internal static string FormatPlatform_Windows8Required()
|
||||
{
|
||||
return GetString("Platform_Windows8Required");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -123,4 +123,10 @@
|
|||
<data name="BCRYPT_KEY_LENGTHS_STRUCT_InvalidKeyLength" xml:space="preserve">
|
||||
<value>The key length {0} is invalid. Valid key lengths are {1} to {2} bits (step size {3}).</value>
|
||||
</data>
|
||||
<data name="Platform_Windows7Required" xml:space="preserve">
|
||||
<value>This operation requires Windows 7 / Windows Server 2008 R2 or later.</value>
|
||||
</data>
|
||||
<data name="Platform_Windows8Required" xml:space="preserve">
|
||||
<value>This operation requires Windows 8 / Windows Server 2012 or later.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -10,6 +10,9 @@ using Microsoft.AspNet.Cryptography.Internal;
|
|||
|
||||
namespace Microsoft.AspNet.Cryptography.SafeHandles
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a handle to a BCrypt algorithm provider from which keys and hashes can be created.
|
||||
/// </summary>
|
||||
internal unsafe sealed class BCryptAlgorithmHandle : BCryptHandle
|
||||
{
|
||||
// Called by P/Invoke when returning SafeHandles
|
||||
|
|
@ -20,10 +23,10 @@ namespace Microsoft.AspNet.Cryptography.SafeHandles
|
|||
/// </summary>
|
||||
public BCryptHashHandle CreateHash()
|
||||
{
|
||||
return CreateHashImpl(null, 0);
|
||||
return CreateHashCore(null, 0);
|
||||
}
|
||||
|
||||
private BCryptHashHandle CreateHashImpl(byte* pbKey, uint cbKey)
|
||||
private BCryptHashHandle CreateHashCore(byte* pbKey, uint cbKey)
|
||||
{
|
||||
BCryptHashHandle retVal;
|
||||
int ntstatus = UnsafeNativeMethods.BCryptCreateHash(this, out retVal, IntPtr.Zero, 0, pbKey, cbKey, dwFlags: 0);
|
||||
|
|
@ -40,7 +43,7 @@ namespace Microsoft.AspNet.Cryptography.SafeHandles
|
|||
public BCryptHashHandle CreateHmac(byte* pbKey, uint cbKey)
|
||||
{
|
||||
Debug.Assert(pbKey != null);
|
||||
return CreateHashImpl(pbKey, cbKey);
|
||||
return CreateHashCore(pbKey, cbKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -6,13 +6,33 @@ using Microsoft.Win32.SafeHandles;
|
|||
|
||||
namespace Microsoft.AspNet.Cryptography.SafeHandles
|
||||
{
|
||||
internal sealed class NCryptDescriptorHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
internal unsafe sealed class NCryptDescriptorHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private NCryptDescriptorHandle()
|
||||
: base(ownsHandle: true)
|
||||
{
|
||||
}
|
||||
|
||||
public string GetProtectionDescriptorRuleString()
|
||||
{
|
||||
// from ncryptprotect.h
|
||||
const int NCRYPT_PROTECTION_INFO_TYPE_DESCRIPTOR_STRING = 0x00000001;
|
||||
|
||||
LocalAllocHandle ruleStringHandle;
|
||||
int ntstatus = UnsafeNativeMethods.NCryptGetProtectionDescriptorInfo(
|
||||
hDescriptor: this,
|
||||
pMemPara: IntPtr.Zero,
|
||||
dwInfoType: NCRYPT_PROTECTION_INFO_TYPE_DESCRIPTOR_STRING,
|
||||
ppvInfo: out ruleStringHandle);
|
||||
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
||||
CryptoUtil.AssertSafeHandleIsValid(ruleStringHandle);
|
||||
|
||||
using (ruleStringHandle)
|
||||
{
|
||||
return new String((char*)ruleStringHandle.DangerousGetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Microsoft.AspNet.Cryptography.SafeHandles
|
||||
{
|
||||
internal sealed class SafeCertContextHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private SafeCertContextHandle()
|
||||
: base(ownsHandle: true)
|
||||
{
|
||||
}
|
||||
|
||||
public static SafeCertContextHandle CreateDuplicateFrom(IntPtr existingHandle)
|
||||
{
|
||||
SafeCertContextHandle newHandle = UnsafeNativeMethods.CertDuplicateCertificateContext(existingHandle);
|
||||
CryptoUtil.AssertSafeHandleIsValid(newHandle);
|
||||
return newHandle;
|
||||
}
|
||||
|
||||
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return UnsafeNativeMethods.CertFreeCertificateContext(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
#if DNXCORE50
|
||||
namespace Microsoft.AspNet.Cryptography.SafeHandles
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a managed view over an NCRYPT_KEY_HANDLE.
|
||||
/// </summary>
|
||||
internal class SafeNCryptKeyHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
// Called by P/Invoke when returning SafeHandles
|
||||
protected SafeNCryptKeyHandle()
|
||||
: base(ownsHandle: true) { }
|
||||
|
||||
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
// TODO: Replace me with a real implementation on CoreClr.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2,9 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
#if !DNXCORE50
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ namespace Microsoft.AspNet.Cryptography
|
|||
{
|
||||
internal unsafe static class UnsafeBufferUtil
|
||||
{
|
||||
private static readonly byte[] _emptyArray = new byte[0];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#if !DNXCORE50
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
|
|
@ -33,7 +31,7 @@ namespace Microsoft.AspNet.Cryptography
|
|||
{
|
||||
if (byteCount != 0)
|
||||
{
|
||||
BlockCopyImpl((byte*)from, (byte*)to, byteCount);
|
||||
BlockCopyCore((byte*)from, (byte*)to, byteCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +58,7 @@ namespace Microsoft.AspNet.Cryptography
|
|||
#if !DNXCORE50
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
#endif
|
||||
public static void BlockCopy(byte* from, LocalAllocHandle to, uint byteCount)
|
||||
public static void BlockCopy(void* from, LocalAllocHandle to, uint byteCount)
|
||||
{
|
||||
bool refAdded = false;
|
||||
try
|
||||
|
|
@ -95,10 +93,11 @@ namespace Microsoft.AspNet.Cryptography
|
|||
to.DangerousAddRef(ref toRefAdded);
|
||||
if (sizeof(IntPtr) == 4)
|
||||
{
|
||||
BlockCopyImpl(from: (byte*)from.DangerousGetHandle(), to: (byte*)to.DangerousGetHandle(), byteCount: (uint)length.ToInt32());
|
||||
} else
|
||||
BlockCopyCore(from: (byte*)from.DangerousGetHandle(), to: (byte*)to.DangerousGetHandle(), byteCount: (uint)length.ToInt32());
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockCopyImpl(from: (byte*)from.DangerousGetHandle(), to: (byte*)to.DangerousGetHandle(), byteCount: (ulong)length.ToInt64());
|
||||
BlockCopyCore(from: (byte*)from.DangerousGetHandle(), to: (byte*)to.DangerousGetHandle(), byteCount: (ulong)length.ToInt64());
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
|
@ -115,24 +114,26 @@ namespace Microsoft.AspNet.Cryptography
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void BlockCopyImpl(byte* from, byte* to, uint byteCount)
|
||||
private static void BlockCopyCore(byte* from, byte* to, uint byteCount)
|
||||
{
|
||||
#if DNXCORE50
|
||||
Buffer.MemoryCopy(from, to, (ulong)byteCount, (ulong)byteCount);
|
||||
#else
|
||||
while (byteCount-- != 0) {
|
||||
while (byteCount-- != 0)
|
||||
{
|
||||
to[byteCount] = from[byteCount];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void BlockCopyImpl(byte* from, byte* to, ulong byteCount)
|
||||
private static void BlockCopyCore(byte* from, byte* to, ulong byteCount)
|
||||
{
|
||||
#if DNXCORE50
|
||||
Buffer.MemoryCopy(from, to, byteCount, byteCount);
|
||||
#else
|
||||
while (byteCount-- != 0) {
|
||||
while (byteCount-- != 0)
|
||||
{
|
||||
to[byteCount] = from[byteCount];
|
||||
}
|
||||
#endif
|
||||
|
|
@ -209,33 +210,5 @@ namespace Microsoft.AspNet.Cryptography
|
|||
SecureZeroMemory(buffer, (ulong)length.ToInt64());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new managed byte[] from unmanaged memory.
|
||||
/// </summary>
|
||||
public static byte[] ToManagedByteArray(byte* ptr, int byteCount)
|
||||
{
|
||||
return ToManagedByteArray(ptr, checked((uint)byteCount));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new managed byte[] from unmanaged memory.
|
||||
/// </summary>
|
||||
public static byte[] ToManagedByteArray(byte* ptr, uint byteCount)
|
||||
{
|
||||
if (byteCount == 0)
|
||||
{
|
||||
return _emptyArray; // degenerate case
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] bytes = new byte[byteCount];
|
||||
fixed (byte* pBytes = bytes)
|
||||
{
|
||||
BlockCopy(from: ptr, to: pBytes, byteCount: byteCount);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,44 +200,7 @@ namespace Microsoft.AspNet.Cryptography
|
|||
/*
|
||||
* CRYPT32.DLL
|
||||
*/
|
||||
|
||||
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi)]
|
||||
#if !DNXCORE50
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
#endif
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa376045(v=vs.85).aspx
|
||||
internal static extern SafeCertContextHandle CertDuplicateCertificateContext(
|
||||
[In] IntPtr pCertContext);
|
||||
|
||||
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi)]
|
||||
#if !DNXCORE50
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
#endif
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa376075(v=vs.85).aspx
|
||||
internal static extern bool CertFreeCertificateContext(
|
||||
[In] IntPtr pCertContext);
|
||||
|
||||
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa376079(v=vs.85).aspx
|
||||
internal static extern bool CertGetCertificateContextProperty(
|
||||
[In] SafeCertContextHandle pCertContext,
|
||||
[In] uint dwPropId,
|
||||
[In] void* pvData,
|
||||
[In, Out] ref uint pcbData);
|
||||
|
||||
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa379885(v=vs.85).aspx
|
||||
#if !DNXCORE50
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
#endif
|
||||
internal static extern bool CryptAcquireCertificatePrivateKey(
|
||||
[In] SafeCertContextHandle pCert,
|
||||
[In] uint dwFlags,
|
||||
[In] void* pvParameters,
|
||||
[Out] out SafeNCryptKeyHandle phCryptProvOrNCryptKey,
|
||||
[Out] out uint pdwKeySpec,
|
||||
[Out] out bool pfCallerFreeProvOrNCryptKey);
|
||||
|
||||
|
||||
[DllImport(CRYPT32_LIB, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261(v=vs.85).aspx
|
||||
internal static extern bool CryptProtectData(
|
||||
|
|
@ -301,16 +264,12 @@ namespace Microsoft.AspNet.Cryptography
|
|||
[Out] out NCryptDescriptorHandle phDescriptor);
|
||||
|
||||
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa376249(v=vs.85).aspx
|
||||
internal static extern int NCryptDecrypt(
|
||||
[In] SafeNCryptKeyHandle hKey,
|
||||
[In] byte* pbInput,
|
||||
[In] uint cbInput,
|
||||
[In] void* pPaddingInfo,
|
||||
[In] byte* pbOutput,
|
||||
[In] uint cbOutput,
|
||||
[Out] out uint pcbResult,
|
||||
[In] NCryptEncryptFlags dwFlags);
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh706801(v=vs.85).aspx
|
||||
internal static extern int NCryptGetProtectionDescriptorInfo(
|
||||
[In] NCryptDescriptorHandle hDescriptor,
|
||||
[In] IntPtr pMemPara,
|
||||
[In] uint dwInfoType,
|
||||
[Out] out LocalAllocHandle ppvInfo);
|
||||
|
||||
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706802(v=vs.85).aspx
|
||||
|
|
@ -336,6 +295,18 @@ namespace Microsoft.AspNet.Cryptography
|
|||
[Out] out LocalAllocHandle ppbData,
|
||||
[Out] out uint pcbData);
|
||||
|
||||
[DllImport(NCRYPT_LIB, CallingConvention = CallingConvention.Winapi)]
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706811(v=vs.85).aspx
|
||||
internal static extern int NCryptUnprotectSecret(
|
||||
[Out] out NCryptDescriptorHandle phDescriptor,
|
||||
[In] uint dwFlags,
|
||||
[In] byte* pbProtectedBlob,
|
||||
[In] uint cbProtectedBlob,
|
||||
[In] IntPtr pMemPara,
|
||||
[In] IntPtr hWnd,
|
||||
[Out] out LocalAllocHandle ppbData,
|
||||
[Out] out uint pcbData);
|
||||
|
||||
/*
|
||||
* HELPER FUNCTIONS
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,8 +6,24 @@ using Microsoft.AspNet.Cryptography.KeyDerivation.PBKDF2;
|
|||
|
||||
namespace Microsoft.AspNet.Cryptography.KeyDerivation
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides algorithms for performing key derivation.
|
||||
/// </summary>
|
||||
public static class KeyDerivation
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs key derivation using the PBKDF2 algorithm.
|
||||
/// </summary>
|
||||
/// <param name="password">The password from which to derive the key.</param>
|
||||
/// <param name="salt">The salt to be used during the key derivation process.</param>
|
||||
/// <param name="prf">The pseudo-random function to be used in the key derivation process.</param>
|
||||
/// <param name="iterationCount">The number of iterations of the pseudo-random function to apply
|
||||
/// during the key derivation process.</param>
|
||||
/// <param name="numBytesRequested">The desired length (in bytes) of the derived key.</param>
|
||||
/// <returns>The derived key.</returns>
|
||||
/// <remarks>
|
||||
/// The PBKDF2 algorithm is specified in RFC 2898.
|
||||
/// </remarks>
|
||||
public static byte[] Pbkdf2(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested)
|
||||
{
|
||||
// parameter checking
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ namespace Microsoft.AspNet.Cryptography.KeyDerivation.PBKDF2
|
|||
private static IPbkdf2Provider GetPbkdf2Provider()
|
||||
{
|
||||
// In priority order, our three implementations are Win8, Win7, and "other".
|
||||
if (OSVersionUtil.IsBCryptOnWin8OrLaterAvailable())
|
||||
if (OSVersionUtil.IsWindows8OrLater())
|
||||
{
|
||||
// fastest implementation
|
||||
return new Win8Pbkdf2Provider();
|
||||
} else if (OSVersionUtil.IsBCryptOnWin7OrLaterAvailable())
|
||||
} else if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// acceptable implementation
|
||||
return new Win7Pbkdf2Provider();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Net;
|
|||
using System.Runtime.ExceptionServices;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNet.DataProtection.Repositories;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.WindowsAzure.Storage;
|
||||
using Microsoft.WindowsAzure.Storage.Blob;
|
||||
|
|
@ -49,7 +50,7 @@ namespace Microsoft.AspNet.DataProtection.Azure
|
|||
{
|
||||
var blobRef = GetKeyRingBlockBlobReference();
|
||||
XDocument document = ReadDocumentFromStorage(blobRef);
|
||||
return document?.Root.Elements().ToArray() ?? new XElement[0];
|
||||
return (IReadOnlyCollection<XElement>)document?.Root.Elements().ToList().AsReadOnly() ?? new XElement[0];
|
||||
}
|
||||
|
||||
private XDocument ReadDocumentFromStorage(CloudBlockBlob blobRef)
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.Azure
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
||||
internal sealed class NotNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
"description": "ASP.NET 5 blob storage repository for DataProtection.",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.DataProtection": "1.0.0-*",
|
||||
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" },
|
||||
"WindowsAzure.Storage": "4.3.0"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.Compatibility
|
||||
{
|
||||
internal sealed class DataProtectionProviderHelper
|
||||
{
|
||||
private IDataProtectionProvider _dataProtectionProvider;
|
||||
|
||||
private DataProtectionProviderHelper() { } // can only be instantaited by self
|
||||
|
||||
public static IDataProtectionProvider GetDataProtectionProvider(ref DataProtectionProviderHelper helperRef, IFactorySupportFunctions supportFunctions)
|
||||
{
|
||||
// First, make sure that only one thread ever initializes the helper instance.
|
||||
var helper = Volatile.Read(ref helperRef);
|
||||
if (helper == null)
|
||||
{
|
||||
var newHelper = new DataProtectionProviderHelper();
|
||||
helper = Interlocked.CompareExchange(ref helperRef, newHelper, null) ?? newHelper;
|
||||
}
|
||||
|
||||
// Has the provider already been created?
|
||||
var provider = Volatile.Read(ref helper._dataProtectionProvider);
|
||||
if (provider == null)
|
||||
{
|
||||
// Since the helper is accessed by reference, all threads should agree on the one true helper
|
||||
// instance, so this lock is global given a particular reference. This is an implementation
|
||||
// of the double-check lock pattern.
|
||||
lock (helper)
|
||||
{
|
||||
provider = Volatile.Read(ref helper._dataProtectionProvider);
|
||||
if (provider == null)
|
||||
{
|
||||
provider = supportFunctions.CreateDataProtectionProvider();
|
||||
Volatile.Write(ref helper._dataProtectionProvider, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And we're done!
|
||||
Debug.Assert(provider != null);
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.Compatibility
|
||||
{
|
||||
public sealed class DataProtector<T> : DataProtector, IFactorySupportFunctions
|
||||
where T : class, IDataProtectionProviderFactory, new()
|
||||
{
|
||||
private static DataProtectionProviderHelper _staticHelper;
|
||||
private DataProtectorHelper _helper;
|
||||
|
||||
public DataProtector(string applicationName, string primaryPurpose, string[] specificPurposes)
|
||||
: base(applicationName, primaryPurpose, specificPurposes)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool PrependHashedPurposeToPlaintext
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private IDataProtector GetCachedDataProtector()
|
||||
{
|
||||
var dataProtectionProvider = DataProtectionProviderHelper.GetDataProtectionProvider(ref _staticHelper, this);
|
||||
return DataProtectorHelper.GetDataProtector(ref _helper, dataProtectionProvider, this);
|
||||
}
|
||||
|
||||
public override bool IsReprotectRequired(byte[] encryptedData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override byte[] ProviderProtect(byte[] userData)
|
||||
{
|
||||
return GetCachedDataProtector().Protect(userData);
|
||||
}
|
||||
|
||||
protected override byte[] ProviderUnprotect(byte[] encryptedData)
|
||||
{
|
||||
return GetCachedDataProtector().Unprotect(encryptedData);
|
||||
}
|
||||
|
||||
IDataProtectionProvider IFactorySupportFunctions.CreateDataProtectionProvider()
|
||||
{
|
||||
IDataProtectionProviderFactory factory = Activator.CreateInstance<T>();
|
||||
IDataProtectionProvider dataProtectionProvider = factory.CreateDataProtectionProvider();
|
||||
Debug.Assert(dataProtectionProvider != null);
|
||||
return dataProtectionProvider;
|
||||
}
|
||||
|
||||
IDataProtector IFactorySupportFunctions.CreateDataProtector(IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
Debug.Assert(dataProtectionProvider != null);
|
||||
|
||||
IDataProtector dataProtector = dataProtectionProvider.CreateProtector(ApplicationName).CreateProtector(PrimaryPurpose);
|
||||
foreach (string specificPurpose in SpecificPurposes)
|
||||
{
|
||||
dataProtector = dataProtector.CreateProtector(specificPurpose);
|
||||
}
|
||||
|
||||
Debug.Assert(dataProtector != null);
|
||||
return dataProtector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.Compatibility
|
||||
{
|
||||
internal sealed class DataProtectorHelper
|
||||
{
|
||||
private IDataProtector _dataProtector;
|
||||
|
||||
private DataProtectorHelper() { } // can only be instantaited by self
|
||||
|
||||
public static IDataProtector GetDataProtector(ref DataProtectorHelper helperRef, IDataProtectionProvider protectionProvider, IFactorySupportFunctions supportFunctions)
|
||||
{
|
||||
// First, make sure that only one thread ever initializes the helper instance.
|
||||
var helper = Volatile.Read(ref helperRef);
|
||||
if (helper == null)
|
||||
{
|
||||
var newHelper = new DataProtectorHelper();
|
||||
helper = Interlocked.CompareExchange(ref helperRef, newHelper, null) ?? newHelper;
|
||||
}
|
||||
|
||||
// Has the protector already been created?
|
||||
var protector = Volatile.Read(ref helper._dataProtector);
|
||||
if (protector == null)
|
||||
{
|
||||
// Since the helper is accessed by reference, all threads should agree on the one true helper
|
||||
// instance, so this lock is global given a particular reference. This is an implementation
|
||||
// of the double-check lock pattern.
|
||||
lock (helper)
|
||||
{
|
||||
protector = Volatile.Read(ref helper._dataProtector);
|
||||
if (protector == null)
|
||||
{
|
||||
protector = supportFunctions.CreateDataProtector(protectionProvider);
|
||||
Volatile.Write(ref helper._dataProtector, protector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And we're done!
|
||||
Debug.Assert(protector != null);
|
||||
return protector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.Compatibility
|
||||
{
|
||||
internal interface IFactorySupportFunctions
|
||||
{
|
||||
IDataProtectionProvider CreateDataProtectionProvider();
|
||||
|
||||
IDataProtector CreateDataProtector(IDataProtectionProvider dataProtectionProvider);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
internal static class CryptoUtil
|
||||
{
|
||||
// This isn't a typical Debug.Fail; an error always occurs, even in retail builds.
|
||||
// This method doesn't return, but since the CLR doesn't allow specifying a 'never'
|
||||
// return type, we mimic it by specifying our return type as Exception. That way
|
||||
// callers can write 'throw Fail(...);' to make the C# compiler happy, as the
|
||||
// throw keyword is implicitly of type O.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static Exception Fail(string message)
|
||||
{
|
||||
Debug.Fail(message);
|
||||
throw new CryptographicException("Assertion failed: " + message);
|
||||
}
|
||||
|
||||
// Allows callers to write "var x = Method() ?? Fail<T>(message);" as a convenience to guard
|
||||
// against a method returning null unexpectedly.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static T Fail<T>(string message) where T : class
|
||||
{
|
||||
throw Fail(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNet.DataProtection.Interfaces;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Helpful extension methods for data protection APIs.
|
||||
/// </summary>
|
||||
public static class DataProtectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IDataProtector"/> given a list of purposes.
|
||||
/// </summary>
|
||||
/// <param name="provider">The <see cref="IDataProtectionProvider"/> from which to generate the purpose chain.</param>
|
||||
/// <param name="purposes">The list of purposes which contribute to the purpose chain. This list must
|
||||
/// contain at least one element, and it may not contain null elements.</param>
|
||||
/// <returns>An <see cref="IDataProtector"/> tied to the provided purpose chain.</returns>
|
||||
/// <remarks>
|
||||
/// This is a convenience method which chains together several calls to
|
||||
/// <see cref="IDataProtectionProvider.CreateProtector(string)"/>. See that method's
|
||||
/// documentation for more information.
|
||||
/// </remarks>
|
||||
public static IDataProtector CreateProtector([NotNull] this IDataProtectionProvider provider, [NotNull] IEnumerable<string> purposes)
|
||||
{
|
||||
bool collectionIsEmpty = true;
|
||||
IDataProtectionProvider retVal = provider;
|
||||
foreach (string purpose in purposes)
|
||||
{
|
||||
if (purpose == null)
|
||||
{
|
||||
throw new ArgumentException(Resources.DataProtectionExtensions_NullPurposesCollection, nameof(purposes));
|
||||
}
|
||||
retVal = retVal.CreateProtector(purpose) ?? CryptoUtil.Fail<IDataProtector>("CreateProtector returned null.");
|
||||
collectionIsEmpty = false;
|
||||
}
|
||||
|
||||
if (collectionIsEmpty)
|
||||
{
|
||||
throw new ArgumentException(Resources.DataProtectionExtensions_NullPurposesCollection, nameof(purposes));
|
||||
}
|
||||
|
||||
Debug.Assert(retVal is IDataProtector); // CreateProtector is supposed to return an instance of this interface
|
||||
return (IDataProtector)retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IDataProtector"/> given a list of purposes.
|
||||
/// </summary>
|
||||
/// <param name="provider">The <see cref="IDataProtectionProvider"/> from which to generate the purpose chain.</param>
|
||||
/// <param name="purpose">The primary purpose used to create the <see cref="IDataProtectionProvider"/>.</param>
|
||||
/// <param name="subPurposes">An optional list of secondary purposes which contribute to the purpose chain.
|
||||
/// If this list is provided it cannot contain null elements.</param>
|
||||
/// <returns>An <see cref="IDataProtector"/> tied to the provided purpose chain.</returns>
|
||||
/// <remarks>
|
||||
/// This is a convenience method which chains together several calls to
|
||||
/// <see cref="IDataProtectionProvider.CreateProtector(string)"/>. See that method's
|
||||
/// documentation for more information.
|
||||
/// </remarks>
|
||||
public static IDataProtector CreateProtector([NotNull] this IDataProtectionProvider provider, [NotNull] string purpose, params string[] subPurposes)
|
||||
{
|
||||
// The method signature isn't simply CreateProtector(this IDataProtectionProvider, params string[] purposes)
|
||||
// because we don't want the code provider.CreateProtector() [parameterless] to inadvertently compile.
|
||||
// The actual signature for this method forces at least one purpose to be provided at the call site.
|
||||
|
||||
IDataProtector protector = provider.CreateProtector(purpose);
|
||||
if (subPurposes != null && subPurposes.Length > 0)
|
||||
{
|
||||
protector = protector?.CreateProtector((IEnumerable<string>)subPurposes);
|
||||
}
|
||||
return protector ?? CryptoUtil.Fail<IDataProtector>("CreateProtector returned null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cryptographically protects a piece of plaintext data.
|
||||
/// </summary>
|
||||
/// <param name="protector">The data protector to use for this operation.</param>
|
||||
/// <param name="plaintext">The plaintext data to protect.</param>
|
||||
/// <returns>The protected form of the plaintext data.</returns>
|
||||
public static string Protect([NotNull] this IDataProtector protector, [NotNull] string plaintext)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] plaintextAsBytes = EncodingUtil.SecureUtf8Encoding.GetBytes(plaintext);
|
||||
byte[] protectedDataAsBytes = protector.Protect(plaintextAsBytes);
|
||||
return WebEncoders.Base64UrlEncode(protectedDataAsBytes);
|
||||
}
|
||||
catch (Exception ex) when (ex.RequiresHomogenization())
|
||||
{
|
||||
// Homogenize exceptions to CryptographicException
|
||||
throw Error.CryptCommon_GenericError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cryptographically unprotects a piece of protected data.
|
||||
/// </summary>
|
||||
/// <param name="protector">The data protector to use for this operation.</param>
|
||||
/// <param name="protectedData">The protected data to unprotect.</param>
|
||||
/// <returns>The plaintext form of the protected data.</returns>
|
||||
/// <remarks>
|
||||
/// This method will throw CryptographicException if the input is invalid or malformed.
|
||||
/// </remarks>
|
||||
public static string Unprotect([NotNull] this IDataProtector protector, [NotNull] string protectedData)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] protectedDataAsBytes = WebEncoders.Base64UrlDecode(protectedData);
|
||||
byte[] plaintextAsBytes = protector.Unprotect(protectedDataAsBytes);
|
||||
return EncodingUtil.SecureUtf8Encoding.GetString(plaintextAsBytes);
|
||||
}
|
||||
catch (Exception ex) when (ex.RequiresHomogenization())
|
||||
{
|
||||
// Homogenize exceptions to CryptographicException
|
||||
throw Error.CryptCommon_GenericError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.Interfaces;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
internal static class Error
|
||||
{
|
||||
public static CryptographicException CryptCommon_GenericError(Exception inner = null)
|
||||
{
|
||||
return new CryptographicException(Resources.CryptCommon_GenericError, inner);
|
||||
}
|
||||
|
||||
public static CryptographicException CryptCommon_PayloadInvalid()
|
||||
{
|
||||
string message = Resources.CryptCommon_PayloadInvalid;
|
||||
return new CryptographicException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information used to discriminate applications.
|
||||
/// </summary>
|
||||
public interface IApplicationDiscriminator
|
||||
{
|
||||
/// <summary>
|
||||
/// An identifier that uniquely discriminates this application from all other
|
||||
/// applications on the machine.
|
||||
/// </summary>
|
||||
string Discriminator { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that can be used to create <see cref="IDataProtector"/> instances.
|
||||
/// </summary>
|
||||
public interface IDataProtectionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IDataProtector"/> given a purpose.
|
||||
/// </summary>
|
||||
/// <param name="purposes">
|
||||
/// The purpose to be assigned to the newly-created <see cref="IDataProtector"/>.
|
||||
/// </param>
|
||||
/// <returns>An IDataProtector tied to the provided purpose.</returns>
|
||||
/// <remarks>
|
||||
/// The <paramref name="purpose"/> parameter must be unique for the intended use case; two
|
||||
/// different <see cref="IDataProtector"/> instances created with two different <paramref name="purpose"/>
|
||||
/// values will not be able to decipher each other's payloads. The <paramref name="purpose"/> parameter
|
||||
/// value is not intended to be kept secret.
|
||||
/// </remarks>
|
||||
IDataProtector CreateProtector([NotNull] string purpose);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
// 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
|
|
@ -13,19 +15,18 @@ namespace Microsoft.AspNet.DataProtection
|
|||
/// <summary>
|
||||
/// Cryptographically protects a piece of plaintext data.
|
||||
/// </summary>
|
||||
/// <param name="unprotectedData">The plaintext data to protect.</param>
|
||||
/// <param name="plaintext">The plaintext data to protect.</param>
|
||||
/// <returns>The protected form of the plaintext data.</returns>
|
||||
byte[] Protect(byte[] unprotectedData);
|
||||
byte[] Protect([NotNull] byte[] plaintext);
|
||||
|
||||
/// <summary>
|
||||
/// Cryptographically unprotects a piece of protected data.
|
||||
/// </summary>
|
||||
/// <param name="protectedData">The protected data to unprotect.</param>
|
||||
/// <returns>The plaintext form of the protected data.</returns>
|
||||
/// <remarks>
|
||||
/// Implementations should throw CryptographicException if the protected data is
|
||||
/// invalid or malformed.
|
||||
/// </remarks>
|
||||
byte[] Unprotect(byte[] protectedData);
|
||||
/// <exception cref="CryptographicException">
|
||||
/// Thrown if the protected data is invalid or malformed.
|
||||
/// </exception>
|
||||
byte[] Unprotect([NotNull] byte[] protectedData);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>C2FD9D02-AA0E-45FA-8561-EE357A94B73D</ProjectGuid>
|
||||
<ProjectGuid>4b115bde-b253-46a6-97bf-a8b37b344ff2</ProjectGuid>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
|
@ -2,11 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.Compatibility
|
||||
{
|
||||
public interface IDataProtectionProviderFactory
|
||||
{
|
||||
IDataProtectionProvider CreateDataProtectionProvider();
|
||||
}
|
||||
}
|
||||
// for unit testing
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.DataProtection.Interfaces.Test")]
|
||||
78
src/Microsoft.AspNet.DataProtection.Interfaces/Properties/Resources.Designer.cs
generated
Normal file
78
src/Microsoft.AspNet.DataProtection.Interfaces/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNet.DataProtection.Interfaces
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.DataProtection.Interfaces.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The payload was invalid.
|
||||
/// </summary>
|
||||
internal static string CryptCommon_PayloadInvalid
|
||||
{
|
||||
get { return GetString("CryptCommon_PayloadInvalid"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The payload was invalid.
|
||||
/// </summary>
|
||||
internal static string FormatCryptCommon_PayloadInvalid()
|
||||
{
|
||||
return GetString("CryptCommon_PayloadInvalid");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The purposes collection cannot be null or empty and cannot contain null elements.
|
||||
/// </summary>
|
||||
internal static string DataProtectionExtensions_NullPurposesCollection
|
||||
{
|
||||
get { return GetString("DataProtectionExtensions_NullPurposesCollection"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The purposes collection cannot be null or empty and cannot contain null elements.
|
||||
/// </summary>
|
||||
internal static string FormatDataProtectionExtensions_NullPurposesCollection()
|
||||
{
|
||||
return GetString("DataProtectionExtensions_NullPurposesCollection");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error occurred during a cryptographic operation.
|
||||
/// </summary>
|
||||
internal static string CryptCommon_GenericError
|
||||
{
|
||||
get { return GetString("CryptCommon_GenericError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error occurred during a cryptographic operation.
|
||||
/// </summary>
|
||||
internal static string FormatCryptCommon_GenericError()
|
||||
{
|
||||
return GetString("CryptCommon_GenericError");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CryptCommon_PayloadInvalid" xml:space="preserve">
|
||||
<value>The payload was invalid.</value>
|
||||
</data>
|
||||
<data name="DataProtectionExtensions_NullPurposesCollection" xml:space="preserve">
|
||||
<value>The purposes collection cannot be null or empty and cannot contain null elements.</value>
|
||||
</data>
|
||||
<data name="CryptCommon_GenericError" xml:space="preserve">
|
||||
<value>An error occurred during a cryptographic operation.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.DataProtection
|
|||
/// The input must not contain any whitespace or padding characters.
|
||||
/// Throws FormatException if the input is malformed.
|
||||
/// </remarks>
|
||||
public static byte[] Base64UrlDecode([NotNull] string input)
|
||||
public static byte[] Base64UrlDecode(string input)
|
||||
{
|
||||
// Assumption: input is base64url encoded without padding and contains no whitespace.
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNet.DataProtection
|
|||
/// </summary>
|
||||
/// <param name="input">The binary input to encode.</param>
|
||||
/// <returns>The base64url-encoded form of the input.</returns>
|
||||
public static string Base64UrlEncode([NotNull] byte[] input)
|
||||
public static string Base64UrlEncode(byte[] input)
|
||||
{
|
||||
// Special-case empty input
|
||||
if (input.Length == 0)
|
||||
|
|
@ -126,7 +126,7 @@ namespace Microsoft.AspNet.DataProtection
|
|||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
throw new FormatException("TODO: Malformed input.");
|
||||
throw Error.CryptCommon_PayloadInvalid(); // not valid base64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"description": "Contains the core IDataProtector and IDataProtectionProvider interfaces for ASP.NET 5 Data Protection.",
|
||||
"dependencies": {
|
||||
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" },
|
||||
"Microsoft.AspNet.DataProtection.Shared": { "type": "build", "version": "" }
|
||||
},
|
||||
"frameworks": {
|
||||
"net451": { },
|
||||
"dnx451": { },
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Diagnostics.Debug": "4.0.10-beta-*",
|
||||
"System.Reflection": "4.0.10-beta-*",
|
||||
"System.Resources.ResourceManager": "4.0.0-beta-*",
|
||||
"System.Runtime.Extensions": "4.0.10-beta-*",
|
||||
"System.Security.Cryptography.Encryption": "4.0.0-beta-*",
|
||||
"System.Text.Encoding.Extensions": "4.0.10-beta-*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ using System.Text;
|
|||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
internal unsafe static class EncodingUtil
|
||||
internal static class EncodingUtil
|
||||
{
|
||||
// UTF8 encoding that fails on invalid chars
|
||||
public static readonly UTF8Encoding SecureUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>3277bb22-033f-4010-8131-a515b910caad</ProjectGuid>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"description": "ASP.NET 5 Data Protection shared code.",
|
||||
"dependencies": {
|
||||
},
|
||||
"frameworks": {
|
||||
"net451": { },
|
||||
"dnx451": { },
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Security.Cryptography.Encryption": "4.0.0-beta-*",
|
||||
"System.Text.Encoding.Extensions": "4.0.10-beta-*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared": "**\\*.cs",
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.SystemWeb
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="DataProtector"/> that can be used by ASP.NET 4.x to interact with ASP.NET 5's
|
||||
/// DataProtection stack. This type is for internal use only and shouldn't be directly used by
|
||||
/// developers.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class CompatibilityDataProtector : DataProtector
|
||||
{
|
||||
private static readonly Lazy<IDataProtectionProvider> _lazyProtectionProvider = new Lazy<IDataProtectionProvider>(CreateProtectionProvider);
|
||||
|
||||
private readonly Lazy<IDataProtector> _lazyProtector;
|
||||
|
||||
public CompatibilityDataProtector(string applicationName, string primaryPurpose, string[] specificPurposes)
|
||||
: base("application-name", "primary-purpose", null) // we feed dummy values to the base ctor
|
||||
{
|
||||
// We don't want to evaluate the IDataProtectionProvider factory quite yet,
|
||||
// as we'd rather defer failures to the call to Protect so that we can bubble
|
||||
// up a good error message to the developer.
|
||||
|
||||
_lazyProtector = new Lazy<IDataProtector>(() => _lazyProtectionProvider.Value.CreateProtector(primaryPurpose, specificPurposes));
|
||||
}
|
||||
|
||||
// We take care of flowing purposes ourselves.
|
||||
protected override bool PrependHashedPurposeToPlaintext { get; } = false;
|
||||
|
||||
private static IDataProtectionProvider CreateProtectionProvider()
|
||||
{
|
||||
// Read from <appSettings> the startup type we need to use, then create it
|
||||
const string APPSETTINGS_KEY = "aspnet:dataProtectionStartupType";
|
||||
string startupTypeName = ConfigurationManager.AppSettings[APPSETTINGS_KEY];
|
||||
if (String.IsNullOrEmpty(startupTypeName))
|
||||
{
|
||||
// fall back to default startup type if one hasn't been specified in config
|
||||
startupTypeName = typeof(DataProtectionStartup).AssemblyQualifiedName;
|
||||
}
|
||||
Type startupType = Type.GetType(startupTypeName, throwOnError: true);
|
||||
var startupInstance = (DataProtectionStartup)Activator.CreateInstance(startupType);
|
||||
|
||||
// Use it to initialize the system.
|
||||
return startupInstance.InternalConfigureServicesAndCreateProtectionProvider();
|
||||
}
|
||||
|
||||
public override bool IsReprotectRequired(byte[] encryptedData)
|
||||
{
|
||||
// Nobody ever calls this.
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override byte[] ProviderProtect(byte[] userData)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _lazyProtector.Value.Protect(userData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// System.Web special-cases ConfigurationException errors and allows them to bubble
|
||||
// up to the developer without being homogenized. Since a call to Protect should
|
||||
// never fail, any exceptions here really do imply a misconfiguration.
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
throw new ConfigurationException(Resources.DataProtector_ProtectFailed, ex);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
}
|
||||
|
||||
protected override byte[] ProviderUnprotect(byte[] encryptedData)
|
||||
{
|
||||
return _lazyProtector.Value.Unprotect(encryptedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Configuration;
|
||||
using System.Web;
|
||||
using System.Web.Configuration;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.SystemWeb
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows controlling the configuration of the ASP.NET 5 Data Protection system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Developers should not call these APIs directly. Instead, developers should subclass
|
||||
/// this type and override the <see cref="ConfigureServices(IServiceCollection)"/>
|
||||
/// method or <see cref="CreateDataProtectionProvider(IServiceProvider)"/> methods
|
||||
/// as appropriate.
|
||||
/// </remarks>
|
||||
public class DataProtectionStartup
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures services used by the Data Protection system.
|
||||
/// </summary>
|
||||
/// <param name="services">A mutable collection of services.</param>
|
||||
/// <remarks>
|
||||
/// Developers may override this method to change the default behaviors of
|
||||
/// the Data Protection system.
|
||||
/// </remarks>
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// InternalConfigureServices already takes care of default configuration.
|
||||
// The reason we don't configure default logic in this method is that we don't
|
||||
// want to punish the developer for forgetting to call base.ConfigureServices
|
||||
// from within his own override.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of an <see cref="IDataProtectionProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="services">A collection of services from which to create the <see cref="IDataProtectionProvider"/>.</param>
|
||||
/// <returns>An <see cref="IDataProtectionProvider"/>.</returns>
|
||||
/// <remarks>
|
||||
/// Developers should generally override the <see cref="ConfigureServices(IServiceCollection)"/>
|
||||
/// method instead of this method.
|
||||
/// </remarks>
|
||||
public virtual IDataProtectionProvider CreateDataProtectionProvider(IServiceProvider services)
|
||||
{
|
||||
return services.GetRequiredService<IDataProtectionProvider>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a default implementation of required services, calls the developer's
|
||||
/// configuration overrides, then creates an <see cref="IDataProtectionProvider"/>.
|
||||
/// </summary>
|
||||
internal IDataProtectionProvider InternalConfigureServicesAndCreateProtectionProvider()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddDataProtection();
|
||||
services.Configure<DataProtectionOptions>(options =>
|
||||
{
|
||||
// Try reading the discriminator from <machineKey applicationName="..." /> defined
|
||||
// at the web app root. If the value was set explicitly (even if the value is empty),
|
||||
// honor it as the discriminator. Otherwise, fall back to the metabase config path.
|
||||
var machineKeySection = (MachineKeySection)WebConfigurationManager.GetWebApplicationSection("system.web/machineKey");
|
||||
if (machineKeySection.ElementInformation.Properties["applicationName"].ValueOrigin != PropertyValueOrigin.Default)
|
||||
{
|
||||
options.ApplicationDiscriminator = machineKeySection.ApplicationName;
|
||||
}
|
||||
else
|
||||
{
|
||||
options.ApplicationDiscriminator = HttpRuntime.AppDomainAppId;
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(options.ApplicationDiscriminator))
|
||||
{
|
||||
options.ApplicationDiscriminator = null; // homogenize to null
|
||||
}
|
||||
});
|
||||
|
||||
// Run configuration and get an instance of the provider.
|
||||
ConfigureServices(services);
|
||||
var provider = CreateDataProtectionProvider(services.BuildServiceProvider());
|
||||
if (provider == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.Startup_CreateProviderReturnedNull);
|
||||
}
|
||||
|
||||
// And we're done!
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>e3552deb-4173-43ae-bf69-3c10dff3bab6</ProjectGuid>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
62
src/Microsoft.AspNet.DataProtection.SystemWeb/Properties/Resources.Designer.cs
generated
Normal file
62
src/Microsoft.AspNet.DataProtection.SystemWeb/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNet.DataProtection.SystemWeb
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.DataProtection.SystemWeb.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// A call to Protect failed. This most likely means that the data protection system is misconfigured. See the inner exception for more information.
|
||||
/// </summary>
|
||||
internal static string DataProtector_ProtectFailed
|
||||
{
|
||||
get { return GetString("DataProtector_ProtectFailed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A call to Protect failed. This most likely means that the data protection system is misconfigured. See the inner exception for more information.
|
||||
/// </summary>
|
||||
internal static string FormatDataProtector_ProtectFailed()
|
||||
{
|
||||
return GetString("DataProtector_ProtectFailed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CreateDataProtectionProvider method returned null.
|
||||
/// </summary>
|
||||
internal static string Startup_CreateProviderReturnedNull
|
||||
{
|
||||
get { return GetString("Startup_CreateProviderReturnedNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CreateDataProtectionProvider method returned null.
|
||||
/// </summary>
|
||||
internal static string FormatStartup_CreateProviderReturnedNull()
|
||||
{
|
||||
return GetString("Startup_CreateProviderReturnedNull");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="DataProtector_ProtectFailed" xml:space="preserve">
|
||||
<value>A call to Protect failed. This most likely means that the data protection system is misconfigured. See the inner exception for more information.</value>
|
||||
</data>
|
||||
<data name="Startup_CreateProviderReturnedNull" xml:space="preserve">
|
||||
<value>The CreateDataProtectionProvider method returned null.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -4,10 +4,13 @@
|
|||
"frameworks": {
|
||||
"net451": {
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.DataProtection": "1.0.0-*"
|
||||
"Microsoft.AspNet.DataProtection": "1.0.0-*",
|
||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*"
|
||||
},
|
||||
"frameworkAssemblies": {
|
||||
"System.Security": "4.0.0.0"
|
||||
"System.Configuration": "4.0.0.0",
|
||||
"System.Security": "4.0.0.0",
|
||||
"System.Web": "4.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<!--
|
||||
If you want to customize the behavior of the ASP.NET 5 Data Protection stack, set the
|
||||
"aspnet:dataProtectionStartupType" switch below to be the fully-qualified name of a
|
||||
type which subclasses Microsoft.AspNet.DataProtection.SystemWeb.DataProtectionStartup.
|
||||
-->
|
||||
<add key="aspnet:dataProtectionStartupType" value="" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<machineKey compatibilityMode="Framework45" dataProtectorType="Microsoft.AspNet.DataProtection.SystemWeb.CompatibilityDataProtector, Microsoft.AspNet.DataProtection.SystemWeb" />
|
||||
</system.web>
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
|
||||
using Microsoft.AspNet.Cryptography;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for working with <see cref="IActivator"/>.
|
||||
/// </summary>
|
||||
internal static class ActivatorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of <paramref name="implementationTypeName"/> and ensures
|
||||
/// that it is assignable to <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
public static T CreateInstance<T>(this IActivator activator, [NotNull] string implementationTypeName)
|
||||
where T : class
|
||||
{
|
||||
return activator.CreateInstance(typeof(T), implementationTypeName) as T
|
||||
?? CryptoUtil.Fail<T>("CreateInstance returned null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="IActivator"/> given an <see cref="IServiceProvider"/>.
|
||||
/// Guaranteed to return non-null, even if <paramref name="serviceProvider"/> is null.
|
||||
/// </summary>
|
||||
public static IActivator GetActivator(this IServiceProvider serviceProvider)
|
||||
{
|
||||
return (serviceProvider != null)
|
||||
? (serviceProvider.GetService<IActivator>() ?? new SimpleActivator(serviceProvider))
|
||||
: SimpleActivator.DefaultWithoutServices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A simplified default implementation of <see cref="IActivator"/> that understands
|
||||
/// how to call ctors which take <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
private sealed class SimpleActivator : IActivator
|
||||
{
|
||||
/// <summary>
|
||||
/// A default <see cref="SimpleActivator"/> whose wrapped <see cref="IServiceProvider"/> is null.
|
||||
/// </summary>
|
||||
internal static readonly SimpleActivator DefaultWithoutServices = new SimpleActivator(null);
|
||||
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public SimpleActivator(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public object CreateInstance(Type expectedBaseType, string implementationTypeName)
|
||||
{
|
||||
// Would the assignment even work?
|
||||
var implementationType = Type.GetType(implementationTypeName, throwOnError: true);
|
||||
expectedBaseType.AssertIsAssignableFrom(implementationType);
|
||||
|
||||
// If no IServiceProvider was specified, prefer .ctor() [if it exists]
|
||||
if (_services == null)
|
||||
{
|
||||
var ctorParameterless = implementationType.GetConstructor(Type.EmptyTypes);
|
||||
if (ctorParameterless != null)
|
||||
{
|
||||
return Activator.CreateInstance(implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
// If an IServiceProvider was specified or if .ctor() doesn't exist, prefer .ctor(IServiceProvider) [if it exists]
|
||||
var ctorWhichTakesServiceProvider = implementationType.GetConstructor(new Type[] { typeof(IServiceProvider) });
|
||||
if (ctorWhichTakesServiceProvider != null)
|
||||
{
|
||||
return ctorWhichTakesServiceProvider.Invoke(new[] { _services });
|
||||
}
|
||||
|
||||
// Finally, prefer .ctor() as an ultimate fallback.
|
||||
// This will throw if the ctor cannot be called.
|
||||
return Activator.CreateInstance(implementationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Signifies that the <see cref="RegistryPolicyResolver"/> should bind this property from the registry.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class ApplyPolicyAttribute : Attribute { }
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal static class AlgorithmAssert
|
||||
{
|
||||
// Our analysis re: IV collision resistance for CBC only holds if we're working with block ciphers
|
||||
// with a block length of 64 bits or greater.
|
||||
private const uint SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BITS = 64;
|
||||
|
||||
// Min security bar: encryption algorithm must have a min 128-bit key.
|
||||
private const uint SYMMETRIC_ALG_MIN_KEY_LENGTH_IN_BITS = 128;
|
||||
|
||||
// Min security bar: authentication tag must have at least 128 bits of output.
|
||||
private const uint HASH_ALG_MIN_DIGEST_LENGTH_IN_BITS = 128;
|
||||
|
||||
// Since we're performing some stack allocs based on these buffers, make sure we don't explode.
|
||||
private const uint MAX_SIZE_IN_BITS = Constants.MAX_STACKALLOC_BYTES * 8;
|
||||
|
||||
public static void IsAllowableSymmetricAlgorithmBlockSize(uint blockSizeInBits)
|
||||
{
|
||||
if (!IsValidCore(blockSizeInBits, SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BITS))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatAlgorithmAssert_BadBlockSize(blockSizeInBits));
|
||||
}
|
||||
}
|
||||
|
||||
public static void IsAllowableSymmetricAlgorithmKeySize(uint keySizeInBits)
|
||||
{
|
||||
if (!IsValidCore(keySizeInBits, SYMMETRIC_ALG_MIN_KEY_LENGTH_IN_BITS))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatAlgorithmAssert_BadKeySize(keySizeInBits));
|
||||
}
|
||||
}
|
||||
|
||||
public static void IsAllowableValidationAlgorithmDigestSize(uint digestSizeInBits)
|
||||
{
|
||||
if (!IsValidCore(digestSizeInBits, HASH_ALG_MIN_DIGEST_LENGTH_IN_BITS))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatAlgorithmAssert_BadDigestSize(digestSizeInBits));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidCore(uint value, uint minValue)
|
||||
{
|
||||
return (value % 8 == 0) // must be whole bytes
|
||||
&& (value >= minValue) // must meet our basic security requirements
|
||||
&& (value <= MAX_SIZE_IN_BITS); // mustn't overflow our stack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for configuring authenticated encryption algorithms.
|
||||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptionOptions : IInternalAuthenticatedEncryptionOptions
|
||||
{
|
||||
/// <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;
|
||||
|
||||
/// <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="AuthenticatedEncryptionOptions"/> 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.
|
||||
var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8));
|
||||
try
|
||||
{
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
finally
|
||||
{
|
||||
(encryptor as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HELPER ROUTINES
|
||||
*/
|
||||
|
||||
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret)
|
||||
{
|
||||
return CreateImplementationOptions()
|
||||
.ToConfiguration()
|
||||
.CreateDescriptorFromSecret(secret)
|
||||
.CreateEncryptorInstance();
|
||||
}
|
||||
|
||||
internal IInternalAuthenticatedEncryptionOptions CreateImplementationOptions()
|
||||
{
|
||||
if (IsGcmAlgorithm(EncryptionAlgorithm))
|
||||
{
|
||||
// GCM requires CNG, and CNG is only supported on Windows.
|
||||
if (!OSVersionUtil.IsWindows())
|
||||
{
|
||||
throw new PlatformNotSupportedException(Resources.Platform_WindowsRequiredForGcm);
|
||||
}
|
||||
return new CngGcmAuthenticatedEncryptionOptions()
|
||||
{
|
||||
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OSVersionUtil.IsWindows())
|
||||
{
|
||||
// CNG preferred over managed implementations if running on Windows
|
||||
return new CngCbcAuthenticatedEncryptionOptions()
|
||||
{
|
||||
EncryptionAlgorithm = GetBCryptAlgorithmName(EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm),
|
||||
HashAlgorithm = GetBCryptAlgorithmName(ValidationAlgorithm)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use managed implementations as a fallback
|
||||
return new ManagedAuthenticatedEncryptionOptions()
|
||||
{
|
||||
EncryptionAlgorithmType = GetManagedTypeForAlgorithm(EncryptionAlgorithm),
|
||||
EncryptionAlgorithmKeySize = GetAlgorithmKeySizeInBits(EncryptionAlgorithm),
|
||||
ValidationAlgorithmType = GetManagedTypeForAlgorithm(ValidationAlgorithm)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetAlgorithmKeySizeInBits(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case EncryptionAlgorithm.AES_128_CBC:
|
||||
case EncryptionAlgorithm.AES_128_GCM:
|
||||
return 128;
|
||||
|
||||
case EncryptionAlgorithm.AES_192_CBC:
|
||||
case EncryptionAlgorithm.AES_192_GCM:
|
||||
return 192;
|
||||
|
||||
case EncryptionAlgorithm.AES_256_CBC:
|
||||
case EncryptionAlgorithm.AES_256_GCM:
|
||||
return 256;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBCryptAlgorithmName(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case EncryptionAlgorithm.AES_128_CBC:
|
||||
case EncryptionAlgorithm.AES_192_CBC:
|
||||
case EncryptionAlgorithm.AES_256_CBC:
|
||||
case EncryptionAlgorithm.AES_128_GCM:
|
||||
case EncryptionAlgorithm.AES_192_GCM:
|
||||
case EncryptionAlgorithm.AES_256_GCM:
|
||||
return Constants.BCRYPT_AES_ALGORITHM;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBCryptAlgorithmName(ValidationAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case ValidationAlgorithm.HMACSHA256:
|
||||
return Constants.BCRYPT_SHA256_ALGORITHM;
|
||||
|
||||
case ValidationAlgorithm.HMACSHA512:
|
||||
return Constants.BCRYPT_SHA512_ALGORITHM;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static Type GetManagedTypeForAlgorithm(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case EncryptionAlgorithm.AES_128_CBC:
|
||||
case EncryptionAlgorithm.AES_192_CBC:
|
||||
case EncryptionAlgorithm.AES_256_CBC:
|
||||
case EncryptionAlgorithm.AES_128_GCM:
|
||||
case EncryptionAlgorithm.AES_192_GCM:
|
||||
case EncryptionAlgorithm.AES_256_GCM:
|
||||
return typeof(Aes);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
private static Type GetManagedTypeForAlgorithm(ValidationAlgorithm algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case ValidationAlgorithm.HMACSHA256:
|
||||
return typeof(HMACSHA256);
|
||||
|
||||
case ValidationAlgorithm.HMACSHA512:
|
||||
return typeof(HMACSHA512);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsGcmAlgorithm(EncryptionAlgorithm algorithm)
|
||||
{
|
||||
return (EncryptionAlgorithm.AES_128_GCM <= algorithm && algorithm <= EncryptionAlgorithm.AES_256_GCM);
|
||||
}
|
||||
|
||||
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionOptions.ToConfiguration()
|
||||
{
|
||||
return new AuthenticatedEncryptorConfiguration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.DataProtection.Cng;
|
||||
using Microsoft.AspNet.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
|
|
@ -31,5 +31,25 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
|||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a self-test of this encryptor by running a sample payload through an
|
||||
/// encrypt-then-decrypt operation. Throws if the operation fails.
|
||||
/// </summary>
|
||||
public static void PerformSelfTest(this IAuthenticatedEncryptor encryptor)
|
||||
{
|
||||
// Arrange
|
||||
Guid plaintextAsGuid = Guid.NewGuid();
|
||||
byte[] plaintextAsBytes = plaintextAsGuid.ToByteArray();
|
||||
byte[] aad = Guid.NewGuid().ToByteArray();
|
||||
|
||||
// Act
|
||||
byte[] protectedData = encryptor.Encrypt(new ArraySegment<byte>(plaintextAsBytes), new ArraySegment<byte>(aad));
|
||||
byte[] roundTrippedData = encryptor.Decrypt(new ArraySegment<byte>(protectedData), new ArraySegment<byte>(aad));
|
||||
|
||||
// Assert
|
||||
CryptoUtil.Assert(roundTrippedData != null && roundTrippedData.Length == plaintextAsBytes.Length && plaintextAsGuid == new Guid(roundTrippedData),
|
||||
"Plaintext did not round-trip properly through the authenticated encryptor.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ using System;
|
|||
using Microsoft.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNet.DataProtection.Cng;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for configuring an authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in CBC encryption + HMAC validation modes.
|
||||
/// Windows CNG algorithms in CBC encryption + HMAC authentication modes.
|
||||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptorConfigurationOptions : IInternalConfigurationOptions
|
||||
public sealed class CngCbcAuthenticatedEncryptionOptions : IInternalAuthenticatedEncryptionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the algorithm to use for symmetric encryption.
|
||||
|
|
@ -21,9 +22,11 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
|||
/// 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 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>
|
||||
|
|
@ -34,6 +37,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
|||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string EncryptionAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -44,6 +48,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
|||
/// The key length must be 128 bits or greater.
|
||||
/// The default value is 256.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -56,6 +61,7 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
|||
/// of 128 bits or greater.
|
||||
/// The default value is 'SHA256'.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string HashAlgorithm { get; set; } = Constants.BCRYPT_SHA256_ALGORITHM;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -66,124 +72,109 @@ namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
|||
/// <remarks>
|
||||
/// The default value is null.
|
||||
/// </remarks>
|
||||
[ApplyPolicy]
|
||||
public string HashAlgorithmProvider { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Makes a duplicate of this object, which allows the original object to remain mutable.
|
||||
/// Validates that this <see cref="CngCbcAuthenticatedEncryptionOptions"/> 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>
|
||||
internal CngCbcAuthenticatedEncryptorConfigurationOptions Clone()
|
||||
public void Validate()
|
||||
{
|
||||
return new CngCbcAuthenticatedEncryptorConfigurationOptions()
|
||||
// Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
|
||||
using (var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8)))
|
||||
{
|
||||
EncryptionAlgorithm = this.EncryptionAlgorithm,
|
||||
EncryptionAlgorithmKeySize = this.EncryptionAlgorithmKeySize,
|
||||
EncryptionAlgorithmProvider = this.EncryptionAlgorithmProvider,
|
||||
HashAlgorithm = this.HashAlgorithm,
|
||||
HashAlgorithmProvider = this.HashAlgorithmProvider
|
||||
};
|
||||
encryptor.PerformSelfTest();
|
||||
}
|
||||
}
|
||||
|
||||
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptor([NotNull] ISecret secret)
|
||||
/*
|
||||
* HELPER ROUTINES
|
||||
*/
|
||||
|
||||
internal CbcAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret)
|
||||
{
|
||||
// Create the encryption object
|
||||
string encryptionAlgorithm = GetPropertyValueNotNullOrEmpty(EncryptionAlgorithm, nameof(EncryptionAlgorithm));
|
||||
string encryptionAlgorithmProvider = GetPropertyValueNormalizeToNull(EncryptionAlgorithmProvider);
|
||||
uint encryptionAlgorithmKeySizeInBits = GetKeySizeInBits(EncryptionAlgorithmKeySize);
|
||||
BCryptAlgorithmHandle encryptionAlgorithmHandle = GetEncryptionAlgorithmHandleAndCheckKeySize(encryptionAlgorithm, encryptionAlgorithmProvider, encryptionAlgorithmKeySizeInBits);
|
||||
|
||||
// Create the validation object
|
||||
string hashAlgorithm = GetPropertyValueNotNullOrEmpty(HashAlgorithm, nameof(HashAlgorithm));
|
||||
string hashAlgorithmProvider = GetPropertyValueNormalizeToNull(HashAlgorithmProvider);
|
||||
BCryptAlgorithmHandle hashAlgorithmHandle = GetHashAlgorithmHandle(hashAlgorithm, hashAlgorithmProvider);
|
||||
|
||||
// and we're good to go!
|
||||
return new CbcAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: encryptionAlgorithmHandle,
|
||||
symmetricAlgorithmKeySizeInBytes: encryptionAlgorithmKeySizeInBits / 8,
|
||||
hmacAlgorithmHandle: hashAlgorithmHandle);
|
||||
symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(),
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(EncryptionAlgorithmKeySize / 8),
|
||||
hmacAlgorithmHandle: GetHmacAlgorithmHandle());
|
||||
}
|
||||
|
||||
private static BCryptAlgorithmHandle GetEncryptionAlgorithmHandleAndCheckKeySize(string encryptionAlgorithm, string encryptionAlgorithmProvider, uint keyLengthInBits)
|
||||
private BCryptAlgorithmHandle GetHmacAlgorithmHandle()
|
||||
{
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(HashAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(HashAlgorithm));
|
||||
}
|
||||
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
// Special-case cached providers
|
||||
if (encryptionAlgorithmProvider == null)
|
||||
if (HashAlgorithmProvider == null)
|
||||
{
|
||||
if (encryptionAlgorithm == Constants.BCRYPT_AES_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.AES_CBC; }
|
||||
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(encryptionAlgorithm, encryptionAlgorithmProvider);
|
||||
algorithmHandle.SetChainingMode(Constants.BCRYPT_CHAIN_MODE_CBC);
|
||||
}
|
||||
|
||||
// make sure we're using a block cipher with an appropriate block size
|
||||
uint cipherBlockSizeInBytes = algorithmHandle.GetCipherBlockLength();
|
||||
CryptoUtil.Assert(cipherBlockSizeInBytes >= CbcAuthenticatedEncryptor.SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES,
|
||||
"cipherBlockSizeInBytes >= CbcAuthenticatedEncryptor.SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES");
|
||||
|
||||
// make sure the provided key length is valid
|
||||
algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength(keyLengthInBits);
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
private static BCryptAlgorithmHandle GetHashAlgorithmHandle(string hashAlgorithm, string 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);
|
||||
algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(HashAlgorithm, HashAlgorithmProvider, hmac: true);
|
||||
}
|
||||
|
||||
// Make sure we're using a hash algorithm. We require a minimum 128-bit digest.
|
||||
uint digestSize = algorithmHandle.GetHashDigestLength();
|
||||
CryptoUtil.Assert(digestSize >= CbcAuthenticatedEncryptor.HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES,
|
||||
"digestSize >= CbcAuthenticatedEncryptor.HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES");
|
||||
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(digestSize * 8));
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
private static uint GetKeySizeInBits(int value)
|
||||
private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle()
|
||||
{
|
||||
CryptoUtil.Assert(value >= 0, "value >= 0");
|
||||
CryptoUtil.Assert(value % 8 == 0, "value % 8 == 0");
|
||||
return (uint)value;
|
||||
}
|
||||
|
||||
private static string GetPropertyValueNormalizeToNull(string value)
|
||||
{
|
||||
return (String.IsNullOrEmpty(value)) ? null : value;
|
||||
}
|
||||
|
||||
private static string GetPropertyValueNotNullOrEmpty(string value, string propertyName)
|
||||
{
|
||||
if (String.IsNullOrEmpty(value))
|
||||
// basic argument checking
|
||||
if (String.IsNullOrEmpty(EncryptionAlgorithm))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(propertyName);
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithm));
|
||||
}
|
||||
return value;
|
||||
if (EncryptionAlgorithmKeySize < 0)
|
||||
{
|
||||
throw Error.Common_PropertyMustBeNonNegative(nameof(EncryptionAlgorithmKeySize));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptor IInternalConfigurationOptions.CreateAuthenticatedEncryptor(ISecret secret)
|
||||
IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionOptions.ToConfiguration()
|
||||
{
|
||||
return CreateAuthenticatedEncryptor(secret);
|
||||
return new CngCbcAuthenticatedEncryptorConfiguration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal sealed class CngCbcAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
internal static readonly XNamespace XmlNamespace = XNamespace.Get("http://www.asp.net/2014/dataProtection/cng");
|
||||
internal static readonly XName CbcEncryptorElementName = XmlNamespace.GetName("cbcEncryptor");
|
||||
internal static readonly XName EncryptionElementName = XmlNamespace.GetName("encryption");
|
||||
internal static readonly XName SecretElementName = XmlNamespace.GetName("secret");
|
||||
internal static readonly XName ValidationElementName = XmlNamespace.GetName("validation");
|
||||
|
||||
private readonly CngCbcAuthenticatedEncryptorConfigurationOptions _options;
|
||||
private readonly ISecret _secret;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorConfiguration(CngCbcAuthenticatedEncryptorConfigurationOptions options, ISecret secret)
|
||||
{
|
||||
_options = options;
|
||||
_secret = secret;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return _options.CreateAuthenticatedEncryptor(_secret);
|
||||
}
|
||||
|
||||
private XElement EncryptSecret(IXmlEncryptor encryptor)
|
||||
{
|
||||
// First, create the inner <secret> element.
|
||||
XElement secretElement;
|
||||
byte[] plaintextSecret = new byte[_secret.Length];
|
||||
try
|
||||
{
|
||||
_secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
||||
secretElement = new XElement(SecretElementName, Convert.ToBase64String(plaintextSecret));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(plaintextSecret, 0, plaintextSecret.Length);
|
||||
}
|
||||
|
||||
// Then encrypt it and wrap it in another <secret> element.
|
||||
var encryptedSecretElement = encryptor.Encrypt(secretElement);
|
||||
CryptoUtil.Assert(!String.IsNullOrEmpty((string)encryptedSecretElement.Attribute("decryptor")),
|
||||
@"TODO: <secret> encryption was invalid.");
|
||||
|
||||
return new XElement(SecretElementName, encryptedSecretElement);
|
||||
}
|
||||
|
||||
public XElement ToXml([NotNull] IXmlEncryptor xmlEncryptor)
|
||||
{
|
||||
// <cbcEncryptor reader="{TYPE}">
|
||||
// <encryption algorithm="{STRING}" provider="{STRING}" keyLength="{INT}" />
|
||||
// <validation algorithm="{STRING}" provider="{STRING}" />
|
||||
// <secret>...</secret>
|
||||
// </cbcEncryptor>
|
||||
|
||||
return new XElement(CbcEncryptorElementName,
|
||||
new XAttribute("reader", typeof(CngCbcAuthenticatedEncryptorConfigurationXmlReader).AssemblyQualifiedName),
|
||||
new XElement(EncryptionElementName,
|
||||
new XAttribute("algorithm", _options.EncryptionAlgorithm),
|
||||
new XAttribute("provider", _options.EncryptionAlgorithmProvider ?? String.Empty),
|
||||
new XAttribute("keyLength", _options.EncryptionAlgorithmKeySize)),
|
||||
new XElement(ValidationElementName,
|
||||
new XAttribute("algorithm", _options.HashAlgorithm),
|
||||
new XAttribute("provider", _options.HashAlgorithmProvider ?? String.Empty)),
|
||||
EncryptSecret(xmlEncryptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory that is able to create a CNG-based IAuthenticatedEncryptor
|
||||
/// using CBC encryption + HMAC validation.
|
||||
/// </summary>
|
||||
public unsafe sealed class CngCbcAuthenticatedEncryptorConfigurationFactory : IAuthenticatedEncryptorConfigurationFactory
|
||||
{
|
||||
private readonly CngCbcAuthenticatedEncryptorConfigurationOptions _options;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorConfigurationFactory([NotNull] IOptions<CngCbcAuthenticatedEncryptorConfigurationOptions> optionsAccessor)
|
||||
{
|
||||
_options = optionsAccessor.Options.Clone();
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptorConfiguration CreateNewConfiguration()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
|
||||
return new CngCbcAuthenticatedEncryptorConfiguration(_options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal sealed class CngCbcAuthenticatedEncryptorConfigurationXmlReader : IAuthenticatedEncryptorConfigurationXmlReader
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public CngCbcAuthenticatedEncryptorConfigurationXmlReader(
|
||||
[NotNull] IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptorConfiguration FromXml([NotNull] XElement element)
|
||||
{
|
||||
// <cbcEncryptor reader="{TYPE}">
|
||||
// <encryption algorithm="{STRING}" provider="{STRING}" keyLength="{INT}" />
|
||||
// <validation algorithm="{STRING}" provider="{STRING}" />
|
||||
// <secret>...</secret>
|
||||
// </cbcEncryptor>
|
||||
|
||||
CryptoUtil.Assert(element.Name == CngCbcAuthenticatedEncryptorConfiguration.CbcEncryptorElementName,
|
||||
@"TODO: Bad element.");
|
||||
|
||||
var options = new CngCbcAuthenticatedEncryptorConfigurationOptions();
|
||||
|
||||
// read <encryption> element
|
||||
var encryptionElement = element.Element(CngCbcAuthenticatedEncryptorConfiguration.EncryptionElementName);
|
||||
options.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
options.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider");
|
||||
options.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
|
||||
// read <validation> element
|
||||
var validationElement = element.Element(CngCbcAuthenticatedEncryptorConfiguration.ValidationElementName);
|
||||
options.HashAlgorithm = (string)validationElement.Attribute("algorithm");
|
||||
options.HashAlgorithmProvider = (string)validationElement.Attribute("provider");
|
||||
|
||||
// read the child of the <secret> element, then decrypt it
|
||||
var encryptedSecretElement = element.Element(CngCbcAuthenticatedEncryptorConfiguration.SecretElementName).Elements().Single();
|
||||
var secretElementDecryptorTypeName = (string)encryptedSecretElement.Attribute("decryptor");
|
||||
var secretElementDecryptorType = Type.GetType(secretElementDecryptorTypeName, throwOnError: true);
|
||||
var secretElementDecryptor = (IXmlDecryptor)ActivatorUtilities.CreateInstance(_serviceProvider, secretElementDecryptorType);
|
||||
var decryptedSecretElement = secretElementDecryptor.Decrypt(encryptedSecretElement);
|
||||
CryptoUtil.Assert(decryptedSecretElement.Name == CngCbcAuthenticatedEncryptorConfiguration.SecretElementName,
|
||||
@"TODO: Bad element.");
|
||||
|
||||
byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
|
||||
try
|
||||
{
|
||||
var secret = new Secret(decryptedSecretBytes);
|
||||
return new CngCbcAuthenticatedEncryptorConfiguration(options, secret);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(decryptedSecretBytes, 0, decryptedSecretBytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNet.DataProtection.Cng;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for configuring an authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in GCM encryption + authentication modes.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptionOptions : IInternalAuthenticatedEncryptionOptions
|
||||
{
|
||||
/// <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="CngGcmAuthenticatedEncryptionOptions"/> 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)
|
||||
{
|
||||
return new GcmAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(),
|
||||
symmetricAlgorithmKeySizeInBytes: (uint)(EncryptionAlgorithmKeySize / 8));
|
||||
}
|
||||
|
||||
private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle()
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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 IInternalAuthenticatedEncryptionOptions.ToConfiguration()
|
||||
{
|
||||
return new CngGcmAuthenticatedEncryptorConfiguration(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal sealed class CngGcmAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
internal static readonly XNamespace XmlNamespace = XNamespace.Get("http://www.asp.net/2014/dataProtection/cng");
|
||||
internal static readonly XName EncryptionElementName = XmlNamespace.GetName("encryption");
|
||||
internal static readonly XName GcmEncryptorElementName = XmlNamespace.GetName("gcmEncryptor");
|
||||
internal static readonly XName SecretElementName = XmlNamespace.GetName("secret");
|
||||
|
||||
private readonly CngGcmAuthenticatedEncryptorConfigurationOptions _options;
|
||||
private readonly ISecret _secret;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorConfiguration(CngGcmAuthenticatedEncryptorConfigurationOptions options, ISecret secret)
|
||||
{
|
||||
_options = options;
|
||||
_secret = secret;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return _options.CreateAuthenticatedEncryptor(_secret);
|
||||
}
|
||||
|
||||
private XElement EncryptSecret(IXmlEncryptor encryptor)
|
||||
{
|
||||
// First, create the inner <secret> element.
|
||||
XElement secretElement;
|
||||
byte[] plaintextSecret = new byte[_secret.Length];
|
||||
try
|
||||
{
|
||||
_secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
||||
secretElement = new XElement(SecretElementName, Convert.ToBase64String(plaintextSecret));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(plaintextSecret, 0, plaintextSecret.Length);
|
||||
}
|
||||
|
||||
// Then encrypt it and wrap it in another <secret> element.
|
||||
var encryptedSecretElement = encryptor.Encrypt(secretElement);
|
||||
CryptoUtil.Assert(!String.IsNullOrEmpty((string)encryptedSecretElement.Attribute("decryptor")),
|
||||
@"TODO: <secret> encryption was invalid.");
|
||||
|
||||
return new XElement(SecretElementName, encryptedSecretElement);
|
||||
}
|
||||
|
||||
public XElement ToXml([NotNull] IXmlEncryptor xmlEncryptor)
|
||||
{
|
||||
// <cbcEncryptor reader="{TYPE}">
|
||||
// <encryption algorithm="{STRING}" provider="{STRING}" keyLength="{INT}" />
|
||||
// <secret>...</secret>
|
||||
// </cbcEncryptor>
|
||||
|
||||
return new XElement(GcmEncryptorElementName,
|
||||
new XAttribute("reader", typeof(CngGcmAuthenticatedEncryptorConfigurationXmlReader).AssemblyQualifiedName),
|
||||
new XElement(EncryptionElementName,
|
||||
new XAttribute("algorithm", _options.EncryptionAlgorithm),
|
||||
new XAttribute("provider", _options.EncryptionAlgorithmProvider ?? String.Empty),
|
||||
new XAttribute("keyLength", _options.EncryptionAlgorithmKeySize)),
|
||||
EncryptSecret(xmlEncryptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory that is able to create a CNG-based IAuthenticatedEncryptor
|
||||
/// using CBC encryption + HMAC validation.
|
||||
/// </summary>
|
||||
public unsafe sealed class CngGcmAuthenticatedEncryptorConfigurationFactory : IAuthenticatedEncryptorConfigurationFactory
|
||||
{
|
||||
private readonly CngGcmAuthenticatedEncryptorConfigurationOptions _options;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorConfigurationFactory([NotNull] IOptions<CngGcmAuthenticatedEncryptorConfigurationOptions> optionsAccessor)
|
||||
{
|
||||
_options = optionsAccessor.Options.Clone();
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptorConfiguration CreateNewConfiguration()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
|
||||
return new CngGcmAuthenticatedEncryptorConfiguration(_options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNet.DataProtection.Cng;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for configuring an authenticated encryption mechanism which uses
|
||||
/// Windows CNG encryption algorithms in Galois/Counter Mode.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptorConfigurationOptions : IInternalConfigurationOptions
|
||||
{
|
||||
/// <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 of exactly 128 bits.
|
||||
/// The default value is 'AES'.
|
||||
/// </remarks>
|
||||
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>
|
||||
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>
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// Makes a duplicate of this object, which allows the original object to remain mutable.
|
||||
/// </summary>
|
||||
internal CngGcmAuthenticatedEncryptorConfigurationOptions Clone()
|
||||
{
|
||||
return new CngGcmAuthenticatedEncryptorConfigurationOptions()
|
||||
{
|
||||
EncryptionAlgorithm = this.EncryptionAlgorithm,
|
||||
EncryptionAlgorithmKeySize = this.EncryptionAlgorithmKeySize,
|
||||
EncryptionAlgorithmProvider = this.EncryptionAlgorithmProvider
|
||||
};
|
||||
}
|
||||
|
||||
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptor([NotNull] ISecret secret)
|
||||
{
|
||||
// Create the encryption object
|
||||
string encryptionAlgorithm = GetPropertyValueNotNullOrEmpty(EncryptionAlgorithm, nameof(EncryptionAlgorithm));
|
||||
string encryptionAlgorithmProvider = GetPropertyValueNormalizeToNull(EncryptionAlgorithmProvider);
|
||||
uint encryptionAlgorithmKeySizeInBits = GetKeySizeInBits(EncryptionAlgorithmKeySize);
|
||||
BCryptAlgorithmHandle encryptionAlgorithmHandle = GetEncryptionAlgorithmHandleAndCheckKeySize(encryptionAlgorithm, encryptionAlgorithmProvider, encryptionAlgorithmKeySizeInBits);
|
||||
|
||||
// and we're good to go!
|
||||
return new GcmAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmHandle: encryptionAlgorithmHandle,
|
||||
symmetricAlgorithmKeySizeInBytes: encryptionAlgorithmKeySizeInBits / 8);
|
||||
}
|
||||
|
||||
private static BCryptAlgorithmHandle GetEncryptionAlgorithmHandleAndCheckKeySize(string encryptionAlgorithm, string encryptionAlgorithmProvider, uint keyLengthInBits)
|
||||
{
|
||||
BCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
// 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 block size
|
||||
uint cipherBlockSizeInBytes = algorithmHandle.GetCipherBlockLength();
|
||||
CryptoUtil.Assert(cipherBlockSizeInBytes == 128 / 8, "cipherBlockSizeInBytes == 128 / 8");
|
||||
|
||||
// make sure the provided key length is valid
|
||||
algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength(keyLengthInBits);
|
||||
|
||||
// all good!
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
private static uint GetKeySizeInBits(int value)
|
||||
{
|
||||
CryptoUtil.Assert(value >= 0, "value >= 0");
|
||||
CryptoUtil.Assert(value % 8 == 0, "value % 8 == 0");
|
||||
return (uint)value;
|
||||
}
|
||||
|
||||
private static string GetPropertyValueNormalizeToNull(string value)
|
||||
{
|
||||
return (String.IsNullOrEmpty(value)) ? null : value;
|
||||
}
|
||||
|
||||
private static string GetPropertyValueNotNullOrEmpty(string value, string propertyName)
|
||||
{
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(propertyName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptor IInternalConfigurationOptions.CreateAuthenticatedEncryptor(ISecret secret)
|
||||
{
|
||||
return CreateAuthenticatedEncryptor(secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal sealed class CngGcmAuthenticatedEncryptorConfigurationXmlReader : IAuthenticatedEncryptorConfigurationXmlReader
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public CngGcmAuthenticatedEncryptorConfigurationXmlReader(
|
||||
[NotNull] IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptorConfiguration FromXml([NotNull] XElement element)
|
||||
{
|
||||
// <cbcEncryptor reader="{TYPE}">
|
||||
// <encryption algorithm="{STRING}" provider="{STRING}" keyLength="{INT}" />
|
||||
// <secret>...</secret>
|
||||
// </cbcEncryptor>
|
||||
|
||||
CryptoUtil.Assert(element.Name == CngGcmAuthenticatedEncryptorConfiguration.GcmEncryptorElementName,
|
||||
@"TODO: Bad element.");
|
||||
|
||||
var options = new CngGcmAuthenticatedEncryptorConfigurationOptions();
|
||||
|
||||
// read <encryption> element
|
||||
var encryptionElement = element.Element(CngGcmAuthenticatedEncryptorConfiguration.EncryptionElementName);
|
||||
options.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
options.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider");
|
||||
options.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
|
||||
// read the child of the <secret> element, then decrypt it
|
||||
var encryptedSecretElement = element.Element(CngGcmAuthenticatedEncryptorConfiguration.SecretElementName).Elements().Single();
|
||||
var secretElementDecryptorTypeName = (string)encryptedSecretElement.Attribute("decryptor");
|
||||
var secretElementDecryptorType = Type.GetType(secretElementDecryptorTypeName, throwOnError: true);
|
||||
var secretElementDecryptor = (IXmlDecryptor)ActivatorUtilities.CreateInstance(_serviceProvider, secretElementDecryptorType);
|
||||
var decryptedSecretElement = secretElementDecryptor.Decrypt(encryptedSecretElement);
|
||||
CryptoUtil.Assert(decryptedSecretElement.Name == CngGcmAuthenticatedEncryptorConfiguration.SecretElementName,
|
||||
@"TODO: Bad element.");
|
||||
|
||||
byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
|
||||
try
|
||||
{
|
||||
var secret = new Secret(decryptedSecretBytes);
|
||||
return new CngGcmAuthenticatedEncryptorConfiguration(options, secret);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(decryptedSecretBytes, 0, decryptedSecretBytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generalized authenticated encryption mechanism.
|
||||
/// </summary>
|
||||
public unsafe sealed class AuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
public AuthenticatedEncryptorConfiguration([NotNull] AuthenticatedEncryptionOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public AuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
|
||||
return ((IInternalAuthenticatedEncryptorConfiguration)this).CreateDescriptorFromSecret(secret);
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new AuthenticatedEncryptorDescriptor(Options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="AuthenticatedEncryptionOptions"/> object.
|
||||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
private readonly ISecret _masterKey;
|
||||
private readonly AuthenticatedEncryptionOptions _options;
|
||||
|
||||
public AuthenticatedEncryptorDescriptor([NotNull] AuthenticatedEncryptionOptions options, [NotNull] ISecret masterKey)
|
||||
{
|
||||
_options = options;
|
||||
_masterKey = masterKey;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return _options.CreateAuthenticatedEncryptorInstance(_masterKey);
|
||||
}
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
// <descriptor>
|
||||
// <encryption algorithm="..." />
|
||||
// <validation algorithm="..." /> <!-- only if not GCM -->
|
||||
// <masterKey requiresEncryption="true">...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", _options.EncryptionAlgorithm));
|
||||
|
||||
var validationElement = (AuthenticatedEncryptionOptions.IsGcmAlgorithm(_options.EncryptionAlgorithm))
|
||||
? (object)new XComment(" AES-GCM includes a 128-bit authentication tag, no extra validation algorithm required. ")
|
||||
: (object)new XElement("validation",
|
||||
new XAttribute("algorithm", _options.ValidationAlgorithm));
|
||||
|
||||
var outerElement = new XElement("descriptor",
|
||||
encryptionElement,
|
||||
validationElement,
|
||||
_masterKey.ToMasterKeyElement());
|
||||
|
||||
return new XmlSerializedDescriptorInfo(outerElement, typeof(AuthenticatedEncryptorDescriptorDeserializer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that can deserialize an <see cref="XElement"/> that represents the serialized version
|
||||
/// of an <see cref="AuthenticatedEncryptorDescriptor"/>.
|
||||
/// </summary>
|
||||
public sealed class AuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports the <see cref="AuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
public IAuthenticatedEncryptorDescriptor ImportFromXml([NotNull] XElement element)
|
||||
{
|
||||
// <descriptor>
|
||||
// <encryption algorithm="..." />
|
||||
// <validation algorithm="..." /> <!-- only if not GCM -->
|
||||
// <masterKey requiresEncryption="true">...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var options = new AuthenticatedEncryptionOptions();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
options.EncryptionAlgorithm = (EncryptionAlgorithm)Enum.Parse(typeof(EncryptionAlgorithm), (string)encryptionElement.Attribute("algorithm"));
|
||||
|
||||
// only read <validation> if not GCM
|
||||
if (!AuthenticatedEncryptionOptions.IsGcmAlgorithm(options.EncryptionAlgorithm))
|
||||
{
|
||||
var validationElement = element.Element("validation");
|
||||
options.ValidationAlgorithm = (ValidationAlgorithm)Enum.Parse(typeof(ValidationAlgorithm), (string)validationElement.Attribute("algorithm"));
|
||||
}
|
||||
|
||||
Secret masterKey = ((string)element.Elements("masterKey").Single()).ToSecret();
|
||||
return new AuthenticatedEncryptorDescriptor(options, masterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configured authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in CBC encryption + HMAC authentication modes.
|
||||
/// </summary>
|
||||
public unsafe sealed class CngCbcAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
public CngCbcAuthenticatedEncryptorConfiguration([NotNull] CngCbcAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public CngCbcAuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
|
||||
return ((IInternalAuthenticatedEncryptorConfiguration)this).CreateDescriptorFromSecret(secret);
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new CngCbcAuthenticatedEncryptorDescriptor(Options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="CngCbcAuthenticatedEncryptionOptions"/> object.
|
||||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
public CngCbcAuthenticatedEncryptorDescriptor([NotNull] CngCbcAuthenticatedEncryptionOptions options, [NotNull] ISecret masterKey)
|
||||
{
|
||||
Options = options;
|
||||
MasterKey = masterKey;
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal CngCbcAuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Options.CreateAuthenticatedEncryptorInstance(MasterKey);
|
||||
}
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
// <descriptor>
|
||||
// <!-- Windows CNG-CBC -->
|
||||
// <encryption algorithm="..." keyLength="..." [provider="..."] />
|
||||
// <hash algorithm="..." [provider="..."] />
|
||||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", Options.EncryptionAlgorithm),
|
||||
new XAttribute("keyLength", Options.EncryptionAlgorithmKeySize));
|
||||
if (Options.EncryptionAlgorithmProvider != null)
|
||||
{
|
||||
encryptionElement.SetAttributeValue("provider", Options.EncryptionAlgorithmProvider);
|
||||
}
|
||||
|
||||
var hashElement = new XElement("hash",
|
||||
new XAttribute("algorithm", Options.HashAlgorithm));
|
||||
if (Options.HashAlgorithmProvider != null)
|
||||
{
|
||||
hashElement.SetAttributeValue("provider", Options.HashAlgorithmProvider);
|
||||
}
|
||||
|
||||
var rootElement = new XElement("descriptor",
|
||||
new XComment(" Algorithms provided by Windows CNG, using CBC-mode encryption with HMAC validation "),
|
||||
encryptionElement,
|
||||
hashElement,
|
||||
MasterKey.ToMasterKeyElement());
|
||||
|
||||
return new XmlSerializedDescriptorInfo(rootElement, typeof(CngCbcAuthenticatedEncryptorDescriptorDeserializer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that can deserialize an <see cref="XElement"/> that represents the serialized version
|
||||
/// of an <see cref="CngCbcAuthenticatedEncryptorDescriptor"/>.
|
||||
/// </summary>
|
||||
public sealed class CngCbcAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
public IAuthenticatedEncryptorDescriptor ImportFromXml([NotNull] XElement element)
|
||||
{
|
||||
// <descriptor>
|
||||
// <!-- Windows CNG-CBC -->
|
||||
// <encryption algorithm="..." keyLength="..." [provider="..."] />
|
||||
// <hash algorithm="..." [provider="..."] />
|
||||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var options = new CngCbcAuthenticatedEncryptionOptions();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
options.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
options.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
options.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
|
||||
|
||||
var hashElement = element.Element("hash");
|
||||
options.HashAlgorithm = (string)hashElement.Attribute("algorithm");
|
||||
options.HashAlgorithmProvider = (string)hashElement.Attribute("provider"); // could be null
|
||||
|
||||
Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
|
||||
|
||||
return new CngCbcAuthenticatedEncryptorDescriptor(options, masterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configured authenticated encryption mechanism which uses
|
||||
/// Windows CNG algorithms in GCM encryption + authentication modes.
|
||||
/// </summary>
|
||||
public unsafe sealed class CngGcmAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
public CngGcmAuthenticatedEncryptorConfiguration([NotNull] CngGcmAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public CngGcmAuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
|
||||
return ((IInternalAuthenticatedEncryptorConfiguration)this).CreateDescriptorFromSecret(secret);
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new CngGcmAuthenticatedEncryptorDescriptor(Options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="CngGcmAuthenticatedEncryptionOptions"/> object.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
public CngGcmAuthenticatedEncryptorDescriptor([NotNull] CngGcmAuthenticatedEncryptionOptions options, [NotNull] ISecret masterKey)
|
||||
{
|
||||
Options = options;
|
||||
MasterKey = masterKey;
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal CngGcmAuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Options.CreateAuthenticatedEncryptorInstance(MasterKey);
|
||||
}
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
// <descriptor>
|
||||
// <!-- Windows CNG-GCM -->
|
||||
// <encryption algorithm="..." keyLength="..." [provider="..."] />
|
||||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", Options.EncryptionAlgorithm),
|
||||
new XAttribute("keyLength", Options.EncryptionAlgorithmKeySize));
|
||||
if (Options.EncryptionAlgorithmProvider != null)
|
||||
{
|
||||
encryptionElement.SetAttributeValue("provider", Options.EncryptionAlgorithmProvider);
|
||||
}
|
||||
|
||||
var rootElement = new XElement("descriptor",
|
||||
new XComment(" Algorithms provided by Windows CNG, using GCM mode encryption and validation "),
|
||||
encryptionElement,
|
||||
MasterKey.ToMasterKeyElement());
|
||||
|
||||
return new XmlSerializedDescriptorInfo(rootElement, typeof(CngGcmAuthenticatedEncryptorDescriptorDeserializer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that can deserialize an <see cref="XElement"/> that represents the serialized version
|
||||
/// of an <see cref="CngGcmAuthenticatedEncryptorDescriptor"/>.
|
||||
/// </summary>
|
||||
public sealed class CngGcmAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports the <see cref="CngCbcAuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
public IAuthenticatedEncryptorDescriptor ImportFromXml([NotNull] XElement element)
|
||||
{
|
||||
// <descriptor>
|
||||
// <!-- Windows CNG-GCM -->
|
||||
// <encryption algorithm="..." keyLength="..." [provider="..."] />
|
||||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var options = new CngGcmAuthenticatedEncryptionOptions();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
options.EncryptionAlgorithm = (string)encryptionElement.Attribute("algorithm");
|
||||
options.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
options.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider"); // could be null
|
||||
|
||||
Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
|
||||
|
||||
return new CngGcmAuthenticatedEncryptorDescriptor(options, masterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The basic configuration that serves as a factory for types related to authenticated encryption.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A self-contained descriptor that wraps all information (including secret key
|
||||
/// material) necessary to create an instance of an <see cref="IAuthenticatedEncryptor"/>.
|
||||
/// </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>
|
||||
/// <returns>
|
||||
/// An <see cref="XmlSerializedDescriptorInfo"/> wrapping the <see cref="XElement"/> which represents the serialized
|
||||
/// current descriptor object. The deserializer type must be assignable to <see cref="IAuthenticatedEncryptorDescriptorDeserializer"/>.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If an element contains sensitive information (such as key material), the
|
||||
/// element should be marked via the <see cref="XmlExtensions.MarkAsRequiresEncryption(XElement)" />
|
||||
/// extension method, and the caller should encrypt the element before persisting
|
||||
/// the XML to storage.
|
||||
/// </remarks>
|
||||
XmlSerializedDescriptorInfo ExportToXml();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The basic interface for deserializing an XML element into an <see cref="IAuthenticatedEncryptorDescriptor"/>.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes the specified XML element.
|
||||
/// </summary>
|
||||
/// <param name="element">The element to deserialize.</param>
|
||||
/// <returns>The <see cref="IAuthenticatedEncryptorDescriptor"/> represented by <paramref name="element"/>.</returns>
|
||||
IAuthenticatedEncryptorDescriptor ImportFromXml([NotNull] XElement element);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.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
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IAuthenticatedEncryptorDescriptor"/> instance from this
|
||||
/// configuration given specific secret key material.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IAuthenticatedEncryptorDescriptor CreateDescriptorFromSecret(ISecret secret);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configured authenticated encryption mechanism which uses
|
||||
/// managed <see cref="SymmetricAlgorithm"/> and <see cref="KeyedHashAlgorithm"/> types.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration, IInternalAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
public ManagedAuthenticatedEncryptorConfiguration([NotNull] ManagedAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public ManagedAuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptorDescriptor CreateNewDescriptor()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
|
||||
return ((IInternalAuthenticatedEncryptorConfiguration)this).CreateDescriptorFromSecret(secret);
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptorDescriptor IInternalAuthenticatedEncryptorConfiguration.CreateDescriptorFromSecret(ISecret secret)
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptorDescriptor(Options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Xml.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A descriptor which can create an authenticated encryption system based upon the
|
||||
/// configuration provided by an <see cref="ManagedAuthenticatedEncryptionOptions"/> object.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorDescriptor : IAuthenticatedEncryptorDescriptor
|
||||
{
|
||||
public ManagedAuthenticatedEncryptorDescriptor([NotNull] ManagedAuthenticatedEncryptionOptions options, [NotNull] ISecret masterKey)
|
||||
{
|
||||
Options = options;
|
||||
MasterKey = masterKey;
|
||||
}
|
||||
|
||||
internal ISecret MasterKey { get; }
|
||||
|
||||
internal ManagedAuthenticatedEncryptionOptions Options { get; }
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return Options.CreateAuthenticatedEncryptorInstance(MasterKey);
|
||||
}
|
||||
|
||||
public XmlSerializedDescriptorInfo ExportToXml()
|
||||
{
|
||||
// <descriptor>
|
||||
// <!-- managed implementations -->
|
||||
// <encryption algorithm="..." keyLength="..." />
|
||||
// <validation algorithm="..." />
|
||||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var encryptionElement = new XElement("encryption",
|
||||
new XAttribute("algorithm", TypeToFriendlyName(Options.EncryptionAlgorithmType)),
|
||||
new XAttribute("keyLength", Options.EncryptionAlgorithmKeySize));
|
||||
|
||||
var validationElement = new XElement("validation",
|
||||
new XAttribute("algorithm", TypeToFriendlyName(Options.ValidationAlgorithmType)));
|
||||
|
||||
var rootElement = new XElement("descriptor",
|
||||
new XComment(" Algorithms provided by specified SymmetricAlgorithm and KeyedHashAlgorithm "),
|
||||
encryptionElement,
|
||||
validationElement,
|
||||
MasterKey.ToMasterKeyElement());
|
||||
|
||||
return new XmlSerializedDescriptorInfo(rootElement, typeof(ManagedAuthenticatedEncryptorDescriptorDeserializer));
|
||||
}
|
||||
|
||||
// Any changes to this method should also be be reflected
|
||||
// in ManagedAuthenticatedEncryptorDescriptorDeserializer.FriendlyNameToType.
|
||||
private static string TypeToFriendlyName(Type type)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Xml.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that can deserialize an <see cref="XElement"/> that represents the serialized version
|
||||
/// of an <see cref="ManagedAuthenticatedEncryptorDescriptor"/>.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorDescriptorDeserializer : IAuthenticatedEncryptorDescriptorDeserializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports the <see cref="ManagedAuthenticatedEncryptorDescriptor"/> from serialized XML.
|
||||
/// </summary>
|
||||
public IAuthenticatedEncryptorDescriptor ImportFromXml([NotNull] XElement element)
|
||||
{
|
||||
// <descriptor>
|
||||
// <!-- managed implementations -->
|
||||
// <encryption algorithm="..." keyLength="..." />
|
||||
// <validation algorithm="..." />
|
||||
// <masterKey>...</masterKey>
|
||||
// </descriptor>
|
||||
|
||||
var options = new ManagedAuthenticatedEncryptionOptions();
|
||||
|
||||
var encryptionElement = element.Element("encryption");
|
||||
options.EncryptionAlgorithmType = FriendlyNameToType((string)encryptionElement.Attribute("algorithm"));
|
||||
options.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
|
||||
var validationElement = element.Element("validation");
|
||||
options.ValidationAlgorithmType = FriendlyNameToType((string)validationElement.Attribute("algorithm"));
|
||||
|
||||
Secret masterKey = ((string)element.Element("masterKey")).ToSecret();
|
||||
|
||||
return new ManagedAuthenticatedEncryptorDescriptor(options, masterKey);
|
||||
}
|
||||
|
||||
// Any changes to this method should also be be reflected
|
||||
// in ManagedAuthenticatedEncryptorDescriptor.TypeToFriendlyName.
|
||||
private static Type FriendlyNameToType(string typeName)
|
||||
{
|
||||
if (typeName == nameof(Aes))
|
||||
{
|
||||
return typeof(Aes);
|
||||
}
|
||||
else if (typeName == nameof(HMACSHA1))
|
||||
{
|
||||
return typeof(HMACSHA1);
|
||||
}
|
||||
else if (typeName == nameof(HMACSHA256))
|
||||
{
|
||||
return typeof(HMACSHA256);
|
||||
}
|
||||
else if (typeName == nameof(HMACSHA384))
|
||||
{
|
||||
return typeof(HMACSHA384);
|
||||
}
|
||||
else if (typeName == nameof(HMACSHA512))
|
||||
{
|
||||
return typeof(HMACSHA512);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Type.GetType(typeName, throwOnError: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
internal unsafe static class SecretExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an <see cref="ISecret"/> to an <masterKey> element which is marked
|
||||
/// as requiring encryption.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static XElement ToMasterKeyElement(this ISecret secret)
|
||||
{
|
||||
// Technically we'll be keeping the unprotected secret around in memory as
|
||||
// a string, so it can get moved by the GC, but we should be good citizens
|
||||
// and try to pin / clear our our temporary buffers regardless.
|
||||
byte[] unprotectedSecretRawBytes = new byte[secret.Length];
|
||||
string unprotectedSecretAsBase64String;
|
||||
fixed (byte* __unused__ = unprotectedSecretRawBytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
secret.WriteSecretIntoBuffer(new ArraySegment<byte>(unprotectedSecretRawBytes));
|
||||
unprotectedSecretAsBase64String = Convert.ToBase64String(unprotectedSecretRawBytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(unprotectedSecretRawBytes, 0, unprotectedSecretRawBytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
XElement masterKeyElement = new XElement("masterKey",
|
||||
new XComment(" Warning: the key below is in an unencrypted form. "),
|
||||
new XElement("value", unprotectedSecretAsBase64String));
|
||||
masterKeyElement.MarkAsRequiresEncryption();
|
||||
return masterKeyElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a base64-encoded string into an <see cref="ISecret"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Secret ToSecret(this string base64String)
|
||||
{
|
||||
byte[] unprotectedSecret = Convert.FromBase64String(base64String);
|
||||
fixed (byte* __unused__ = unprotectedSecret)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Secret(unprotectedSecret);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(unprotectedSecret, 0, unprotectedSecret.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
public static class XmlExtensions
|
||||
{
|
||||
internal static bool IsMarkedAsRequiringEncryption(this XElement element)
|
||||
{
|
||||
return ((bool?)element.Attribute(XmlConstants.RequiresEncryptionAttributeName)).GetValueOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the provided <see cref="XElement"/> as requiring encryption before being persisted
|
||||
/// to storage. Use when implementing <see cref="IAuthenticatedEncryptorDescriptor.ExportToXml"/>.
|
||||
/// </summary>
|
||||
public static void MarkAsRequiresEncryption([NotNull] this XElement element)
|
||||
{
|
||||
element.SetAttributeValue(XmlConstants.RequiresEncryptionAttributeName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps an <see cref="XElement"/> that contains the XML-serialized representation of an
|
||||
/// <see cref="IAuthenticatedEncryptorDescriptor"/> along with the type that can be used
|
||||
/// to deserialize it.
|
||||
/// </summary>
|
||||
public sealed class XmlSerializedDescriptorInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of an <see cref="XmlSerializedDescriptorInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="serializedDescriptorElement">The XML-serialized form of the <see cref="IAuthenticatedEncryptorDescriptor"/>.</param>
|
||||
/// <param name="deserializerType">The class whose <see cref="IAuthenticatedEncryptorDescriptorDeserializer.ImportFromXml(XElement)"/>
|
||||
/// method can be used to deserialize <paramref name="serializedDescriptorElement"/>.</param>
|
||||
public XmlSerializedDescriptorInfo([NotNull] XElement serializedDescriptorElement, [NotNull] Type deserializerType)
|
||||
{
|
||||
if (!typeof(IAuthenticatedEncryptorDescriptorDeserializer).IsAssignableFrom(deserializerType))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatTypeExtensions_BadCast(deserializerType.FullName, typeof(IAuthenticatedEncryptorDescriptorDeserializer).FullName),
|
||||
nameof(deserializerType));
|
||||
}
|
||||
|
||||
SerializedDescriptorElement = serializedDescriptorElement;
|
||||
DeserializerType = deserializerType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class whose <see cref="IAuthenticatedEncryptorDescriptorDeserializer.ImportFromXml(XElement)"/>
|
||||
/// method can be used to deserialize the value stored in <see cref="SerializedDescriptorElement"/>.
|
||||
/// </summary>
|
||||
public Type DeserializerType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An XML-serialized representation of an <see cref="IAuthenticatedEncryptorDescriptor"/>.
|
||||
/// </summary>
|
||||
public XElement SerializedDescriptorElement { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a symmetric encryption algorithm to use for providing confidentiality
|
||||
/// to protected payloads.
|
||||
/// </summary>
|
||||
public enum EncryptionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The AES algorithm (FIPS 197) with a 128-bit key running in Cipher Block Chaining mode.
|
||||
/// </summary>
|
||||
AES_128_CBC,
|
||||
|
||||
/// <summary>
|
||||
/// The AES algorithm (FIPS 197) with a 192-bit key running in Cipher Block Chaining mode.
|
||||
/// </summary>
|
||||
AES_192_CBC,
|
||||
|
||||
/// <summary>
|
||||
/// The AES algorithm (FIPS 197) with a 256-bit key running in Cipher Block Chaining mode.
|
||||
/// </summary>
|
||||
AES_256_CBC,
|
||||
|
||||
/// <summary>
|
||||
/// The AES algorithm (FIPS 197) with a 128-bit key running in Galois/Counter Mode (FIPS SP 800-38D).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cipher mode produces a 128-bit authentication tag. This algorithm is currently only
|
||||
/// supported on Windows.
|
||||
/// </remarks>
|
||||
AES_128_GCM,
|
||||
|
||||
/// <summary>
|
||||
/// The AES algorithm (FIPS 197) with a 192-bit key running in Galois/Counter Mode (FIPS SP 800-38D).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cipher mode produces a 128-bit authentication tag.
|
||||
/// </remarks>
|
||||
AES_192_GCM,
|
||||
|
||||
/// <summary>
|
||||
/// The AES algorithm (FIPS 197) with a 256-bit key running in Galois/Counter Mode (FIPS SP 800-38D).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cipher mode produces a 128-bit authentication tag.
|
||||
/// </remarks>
|
||||
AES_256_GCM,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.XmlEncryption;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a type that contains configuration information about an IAuthenticatedEncryptor
|
||||
/// instance, including how to serialize it to XML.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new IAuthenticatedEncryptor instance based on the current configuration.
|
||||
/// </summary>
|
||||
/// <returns>An IAuthenticatedEncryptor instance.</returns>
|
||||
IAuthenticatedEncryptor CreateEncryptorInstance();
|
||||
|
||||
/// <summary>
|
||||
/// Exports the current configuration to XML, optionally encrypting secret key material.
|
||||
/// </summary>
|
||||
/// <param name="xmlEncryptor">The XML encryptor used to encrypt secret material.</param>
|
||||
/// <returns>An XElement representing the current configuration object.</returns>
|
||||
XElement ToXml(IXmlEncryptor xmlEncryptor);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a type that can create new authenticated encryption configuration objects.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorConfigurationFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new configuration object with fresh secret key material.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An IAuthenticatedEncryptorConfiguration instance.
|
||||
/// </returns>
|
||||
IAuthenticatedEncryptorConfiguration CreateNewConfiguration();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a type that can deserialize an XML-serialized IAuthenticatedEncryptorConfiguration.
|
||||
/// </summary>
|
||||
public interface IAuthenticatedEncryptorConfigurationXmlReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes an XML-serialized IAuthenticatedEncryptorConfiguration.
|
||||
/// </summary>
|
||||
/// <param name="element">The XML element to deserialize.</param>
|
||||
/// <returns>The deserialized IAuthenticatedEncryptorConfiguration.</returns>
|
||||
IAuthenticatedEncryptorConfiguration FromXml(XElement element);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Implemented by our options classes to generalize creating configuration objects.
|
||||
/// </summary>
|
||||
internal interface IInternalAuthenticatedEncryptionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="IInternalAuthenticatedEncryptorConfiguration"/> object
|
||||
/// from the given options.
|
||||
/// </summary>
|
||||
IInternalAuthenticatedEncryptorConfiguration ToConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Performs a self-test of the algorithm specified by the options object.
|
||||
/// </summary>
|
||||
void Validate();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal interface IInternalConfigurationOptions
|
||||
{
|
||||
IAuthenticatedEncryptor CreateAuthenticatedEncryptor(ISecret secret);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||
using Microsoft.AspNet.DataProtection.Managed;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for configuring an authenticated encryption mechanism which uses
|
||||
/// managed SymmetricAlgorithm and KeyedHashAlgorithm implementations.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptionOptions : IInternalAuthenticatedEncryptionOptions
|
||||
{
|
||||
/// <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="ManagedAuthenticatedEncryptionOptions"/> 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)
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmFactory: GetSymmetricBlockCipherAlgorithmFactory(),
|
||||
symmetricAlgorithmKeySizeInBytes: EncryptionAlgorithmKeySize / 8,
|
||||
validationAlgorithmFactory: GetKeyedHashAlgorithmFactory());
|
||||
}
|
||||
|
||||
private Func<KeyedHashAlgorithm> GetKeyedHashAlgorithmFactory()
|
||||
{
|
||||
// basic argument checking
|
||||
if (ValidationAlgorithmType == null)
|
||||
{
|
||||
throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(ValidationAlgorithmType));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
|
||||
if (EncryptionAlgorithmType == typeof(Aes))
|
||||
{
|
||||
Func<Aes> factory = null;
|
||||
#if !DNXCORE50
|
||||
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 IInternalAuthenticatedEncryptionOptions.ToConfiguration()
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptorConfiguration(this);
|
||||
}
|
||||
|
||||
/// <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>;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal sealed class ManagedAuthenticatedEncryptorConfiguration : IAuthenticatedEncryptorConfiguration
|
||||
{
|
||||
internal static readonly XNamespace XmlNamespace = XNamespace.Get("http://www.asp.net/2014/dataProtection/managed");
|
||||
internal static readonly XName ManagedEncryptorElementName = XmlNamespace.GetName("managedEncryptor");
|
||||
internal static readonly XName EncryptionElementName = XmlNamespace.GetName("encryption");
|
||||
internal static readonly XName SecretElementName = XmlNamespace.GetName("secret");
|
||||
internal static readonly XName ValidationElementName = XmlNamespace.GetName("validation");
|
||||
|
||||
private readonly ManagedAuthenticatedEncryptorConfigurationOptions _options;
|
||||
private readonly ISecret _secret;
|
||||
|
||||
public ManagedAuthenticatedEncryptorConfiguration(ManagedAuthenticatedEncryptorConfigurationOptions options, ISecret secret)
|
||||
{
|
||||
_options = options;
|
||||
_secret = secret;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptor CreateEncryptorInstance()
|
||||
{
|
||||
return _options.CreateAuthenticatedEncryptor(_secret);
|
||||
}
|
||||
|
||||
private XElement EncryptSecret(IXmlEncryptor encryptor)
|
||||
{
|
||||
// First, create the inner <secret> element.
|
||||
XElement secretElement;
|
||||
byte[] plaintextSecret = new byte[_secret.Length];
|
||||
try
|
||||
{
|
||||
_secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
||||
secretElement = new XElement(SecretElementName, Convert.ToBase64String(plaintextSecret));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(plaintextSecret, 0, plaintextSecret.Length);
|
||||
}
|
||||
|
||||
// Then encrypt it and wrap it in another <secret> element.
|
||||
var encryptedSecretElement = encryptor.Encrypt(secretElement);
|
||||
CryptoUtil.Assert(!String.IsNullOrEmpty((string)encryptedSecretElement.Attribute("decryptor")),
|
||||
@"TODO: <secret> encryption was invalid.");
|
||||
|
||||
return new XElement(SecretElementName, encryptedSecretElement);
|
||||
}
|
||||
|
||||
public XElement ToXml([NotNull] IXmlEncryptor xmlEncryptor)
|
||||
{
|
||||
// <managedEncryptor reader="{TYPE}">
|
||||
// <encryption type="{TYPE}" keyLength="{INT}" />
|
||||
// <validation type="{TYPE}" />
|
||||
// <secret>...</secret>
|
||||
// </managedEncryptor>
|
||||
|
||||
return new XElement(ManagedEncryptorElementName,
|
||||
new XAttribute("reader", typeof(ManagedAuthenticatedEncryptorConfigurationXmlReader).AssemblyQualifiedName),
|
||||
new XElement(EncryptionElementName,
|
||||
new XAttribute("type", _options.EncryptionAlgorithmType),
|
||||
new XAttribute("keyLength", _options.EncryptionAlgorithmKeySize)),
|
||||
new XElement(ValidationElementName,
|
||||
new XAttribute("type", _options.ValidationAlgorithmType)),
|
||||
EncryptSecret(xmlEncryptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.Managed;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
public sealed class ManagedAuthenticatedEncryptorConfigurationFactory : IAuthenticatedEncryptorConfigurationFactory
|
||||
{
|
||||
private readonly ManagedAuthenticatedEncryptorConfigurationOptions _options;
|
||||
|
||||
public ManagedAuthenticatedEncryptorConfigurationFactory([NotNull] IOptions<ManagedAuthenticatedEncryptorConfigurationOptions> optionsAccessor)
|
||||
{
|
||||
_options = optionsAccessor.Options.Clone();
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptorConfiguration CreateNewConfiguration()
|
||||
{
|
||||
// generate a 512-bit secret randomly
|
||||
const int KDK_SIZE_IN_BYTES = 512 / 8;
|
||||
byte[] kdk = ManagedGenRandomImpl.Instance.GenRandom(KDK_SIZE_IN_BYTES);
|
||||
Secret secret;
|
||||
try
|
||||
{
|
||||
secret = new Secret(kdk);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(kdk, 0, kdk.Length);
|
||||
}
|
||||
|
||||
return new ManagedAuthenticatedEncryptorConfiguration(_options, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.Managed;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for configuring an authenticated encryption mechanism which uses
|
||||
/// managed SymmetricAlgorithm and KeyedHashAlgorithm implementations.
|
||||
/// </summary>
|
||||
public sealed class ManagedAuthenticatedEncryptorConfigurationOptions : IInternalConfigurationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the algorithm to use for symmetric encryption.
|
||||
/// 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>
|
||||
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>
|
||||
public int EncryptionAlgorithmKeySize { get; set; } = 256;
|
||||
|
||||
/// <summary>
|
||||
/// A factory for the algorithm to use for validation.
|
||||
/// 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>
|
||||
public Type ValidationAlgorithmType { get; set; } = typeof(HMACSHA256);
|
||||
|
||||
/// <summary>
|
||||
/// Makes a duplicate of this object, which allows the original object to remain mutable.
|
||||
/// </summary>
|
||||
internal ManagedAuthenticatedEncryptorConfigurationOptions Clone()
|
||||
{
|
||||
return new ManagedAuthenticatedEncryptorConfigurationOptions()
|
||||
{
|
||||
EncryptionAlgorithmType = this.EncryptionAlgorithmType,
|
||||
EncryptionAlgorithmKeySize = this.EncryptionAlgorithmKeySize,
|
||||
ValidationAlgorithmType = this.ValidationAlgorithmType
|
||||
};
|
||||
}
|
||||
|
||||
internal IAuthenticatedEncryptor CreateAuthenticatedEncryptor([NotNull] ISecret secret)
|
||||
{
|
||||
// Create the encryption and validation object
|
||||
Func<SymmetricAlgorithm> encryptorFactory = GetEncryptionAlgorithmFactory();
|
||||
Func<KeyedHashAlgorithm> validatorFactory = GetValidationAlgorithmFactory();
|
||||
|
||||
// Check key size here
|
||||
int keySizeInBits = EncryptionAlgorithmKeySize;
|
||||
CryptoUtil.Assert(keySizeInBits % 8 == 0, "keySizeInBits % 8 == 0");
|
||||
int keySizeInBytes = keySizeInBits / 8;
|
||||
|
||||
// We're good to go!
|
||||
return new ManagedAuthenticatedEncryptor(
|
||||
keyDerivationKey: new Secret(secret),
|
||||
symmetricAlgorithmFactory: encryptorFactory,
|
||||
symmetricAlgorithmKeySizeInBytes: keySizeInBytes,
|
||||
validationAlgorithmFactory: validatorFactory);
|
||||
}
|
||||
|
||||
private Func<SymmetricAlgorithm> GetEncryptionAlgorithmFactory()
|
||||
{
|
||||
CryptoUtil.Assert(EncryptionAlgorithmType != null, "EncryptionAlgorithmType != null");
|
||||
CryptoUtil.Assert(typeof(SymmetricAlgorithm).IsAssignableFrom(EncryptionAlgorithmType), "typeof(SymmetricAlgorithm).IsAssignableFrom(EncryptionAlgorithmType)");
|
||||
|
||||
if (EncryptionAlgorithmType == typeof(Aes))
|
||||
{
|
||||
// On Core CLR, there's no public concrete implementation of AES, so we'll special-case it here
|
||||
return Aes.Create;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise the algorithm must have a default ctor
|
||||
return ((IActivator<SymmetricAlgorithm>)Activator.CreateInstance(typeof(AlgorithmActivator<>).MakeGenericType(EncryptionAlgorithmType))).Creator;
|
||||
}
|
||||
}
|
||||
|
||||
private Func<KeyedHashAlgorithm> GetValidationAlgorithmFactory()
|
||||
{
|
||||
CryptoUtil.Assert(ValidationAlgorithmType != null, "ValidationAlgorithmType != null");
|
||||
CryptoUtil.Assert(typeof(KeyedHashAlgorithm).IsAssignableFrom(ValidationAlgorithmType), "typeof(KeyedHashAlgorithm).IsAssignableFrom(ValidationAlgorithmType)");
|
||||
|
||||
// The algorithm must have a default ctor
|
||||
return ((IActivator<KeyedHashAlgorithm>)Activator.CreateInstance(typeof(AlgorithmActivator<>).MakeGenericType(ValidationAlgorithmType))).Creator;
|
||||
}
|
||||
|
||||
IAuthenticatedEncryptor IInternalConfigurationOptions.CreateAuthenticatedEncryptor(ISecret secret)
|
||||
{
|
||||
return CreateAuthenticatedEncryptor(secret);
|
||||
}
|
||||
|
||||
private interface IActivator<out T>
|
||||
{
|
||||
Func<T> Creator { get; }
|
||||
}
|
||||
|
||||
private class AlgorithmActivator<T> : IActivator<T> where T : new()
|
||||
{
|
||||
public Func<T> Creator { get; } = Activator.CreateInstance<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
internal sealed class ManagedAuthenticatedEncryptorConfigurationXmlReader : IAuthenticatedEncryptorConfigurationXmlReader
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ManagedAuthenticatedEncryptorConfigurationXmlReader(
|
||||
[NotNull] IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IAuthenticatedEncryptorConfiguration FromXml([NotNull] XElement element)
|
||||
{
|
||||
// <managedEncryptor reader="{TYPE}">
|
||||
// <encryption type="{STRING}" keyLength="{INT}" />
|
||||
// <validation type="{STRING}" />
|
||||
// <secret>...</secret>
|
||||
// </managedEncryptor>
|
||||
|
||||
CryptoUtil.Assert(element.Name == ManagedAuthenticatedEncryptorConfiguration.EncryptionElementName,
|
||||
@"TODO: Bad element.");
|
||||
|
||||
var options = new ManagedAuthenticatedEncryptorConfigurationOptions();
|
||||
|
||||
// read <encryption> element
|
||||
var encryptionElement = element.Element(ManagedAuthenticatedEncryptorConfiguration.EncryptionElementName);
|
||||
options.EncryptionAlgorithmType = Type.GetType((string)encryptionElement.Attribute("type"), throwOnError: true);
|
||||
options.EncryptionAlgorithmKeySize = (int)encryptionElement.Attribute("keyLength");
|
||||
|
||||
// read <validation> element
|
||||
var validationElement = element.Element(ManagedAuthenticatedEncryptorConfiguration.ValidationElementName);
|
||||
options.ValidationAlgorithmType = Type.GetType((string)validationElement.Attribute("type"), throwOnError: true);
|
||||
|
||||
// read the child of the <secret> element, then decrypt it
|
||||
var encryptedSecretElement = element.Element(ManagedAuthenticatedEncryptorConfiguration.SecretElementName).Elements().Single();
|
||||
var secretElementDecryptorTypeName = (string)encryptedSecretElement.Attribute("decryptor");
|
||||
var secretElementDecryptorType = Type.GetType(secretElementDecryptorTypeName, throwOnError: true);
|
||||
var secretElementDecryptor = (IXmlDecryptor)ActivatorUtilities.CreateInstance(_serviceProvider, secretElementDecryptorType);
|
||||
var decryptedSecretElement = secretElementDecryptor.Decrypt(encryptedSecretElement);
|
||||
CryptoUtil.Assert(decryptedSecretElement.Name == ManagedAuthenticatedEncryptorConfiguration.SecretElementName,
|
||||
@"TODO: Bad element.");
|
||||
|
||||
byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
|
||||
try
|
||||
{
|
||||
var secret = new Secret(decryptedSecretBytes);
|
||||
return new ManagedAuthenticatedEncryptorConfiguration(options, secret);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(decryptedSecretBytes, 0, decryptedSecretBytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.DataProtection.AuthenticatedEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a message authentication algorithm to use for providing tamper-proofing
|
||||
/// to protected payloads.
|
||||
/// </summary>
|
||||
public enum ValidationAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The HMAC algorithm (RFC 2104) using the SHA-256 hash function (FIPS 180-4).
|
||||
/// </summary>
|
||||
HMACSHA256,
|
||||
|
||||
/// <summary>
|
||||
/// The HMAC algorithm (RFC 2104) using the SHA-512 hash function (FIPS 180-4).
|
||||
/// </summary>
|
||||
HMACSHA512,
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using Microsoft.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNet.DataProtection.SP800_108;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.Cng
|
||||
|
|
@ -25,13 +26,6 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
// probability of collision, and this is acceptable for the expected KDK lifetime.
|
||||
private const uint KEY_MODIFIER_SIZE_IN_BYTES = 128 / 8;
|
||||
|
||||
// Our analysis re: IV collision resistance only holds if we're working with block ciphers
|
||||
// with a block length of 64 bits or greater.
|
||||
internal const uint SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES = 64 / 8;
|
||||
|
||||
// Min security bar: authentication tag must have at least 128 bits of output.
|
||||
internal const uint HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES = 128 / 8;
|
||||
|
||||
private readonly byte[] _contextHeader;
|
||||
private readonly IBCryptGenRandom _genRandom;
|
||||
private readonly BCryptAlgorithmHandle _hmacAlgorithmHandle;
|
||||
|
|
@ -44,9 +38,6 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
|
||||
public CbcAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, BCryptAlgorithmHandle hmacAlgorithmHandle, IBCryptGenRandom genRandom = null)
|
||||
{
|
||||
CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
|
||||
"KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");
|
||||
|
||||
_genRandom = genRandom ?? BCryptGenRandomImpl.Instance;
|
||||
_sp800_108_ctr_hmac_provider = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey);
|
||||
_symmetricAlgorithmHandle = symmetricAlgorithmHandle;
|
||||
|
|
@ -56,14 +47,10 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
_hmacAlgorithmDigestLengthInBytes = hmacAlgorithmHandle.GetHashDigestLength();
|
||||
_hmacAlgorithmSubkeyLengthInBytes = _hmacAlgorithmDigestLengthInBytes; // for simplicity we'll generate HMAC subkeys with a length equal to the digest length
|
||||
|
||||
CryptoUtil.Assert(SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES <= _symmetricAlgorithmBlockSizeInBytes && _symmetricAlgorithmBlockSizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
|
||||
"SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES <= _symmetricAlgorithmBlockSizeInBytes && _symmetricAlgorithmBlockSizeInBytes <= Constants.MAX_STACKALLOC_BYTES");
|
||||
|
||||
CryptoUtil.Assert(HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES <= _hmacAlgorithmDigestLengthInBytes,
|
||||
"HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES <= _hmacAlgorithmDigestLengthInBytes");
|
||||
|
||||
CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= _hmacAlgorithmSubkeyLengthInBytes && _hmacAlgorithmSubkeyLengthInBytes <= Constants.MAX_STACKALLOC_BYTES,
|
||||
"KEY_MODIFIER_SIZE_IN_BYTES <= _hmacAlgorithmSubkeyLengthInBytes && _hmacAlgorithmSubkeyLengthInBytes <= Constants.MAX_STACKALLOC_BYTES");
|
||||
// Argument checking on the algorithms and lengths passed in to us
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmBlockSize(checked(_symmetricAlgorithmBlockSizeInBytes * 8));
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked(_symmetricAlgorithmSubkeyLengthInBytes * 8));
|
||||
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(_hmacAlgorithmDigestLengthInBytes * 8));
|
||||
|
||||
_contextHeader = CreateContextHeader();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,22 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
|
||||
private static readonly byte[] _purpose = Encoding.UTF8.GetBytes("DPAPI-Protected Secret");
|
||||
|
||||
// Probes to see if protecting to the current Windows user account is available.
|
||||
// In theory this should never fail if the user profile is available, so it's more a defense-in-depth check.
|
||||
public static bool CanProtectToCurrentUserAccount()
|
||||
{
|
||||
try
|
||||
{
|
||||
Guid dummy;
|
||||
ProtectWithDpapi(new Secret((byte*)&dummy, sizeof(Guid)), protectToLocalMachine: false);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ProtectWithDpapi(ISecret secret, bool protectToLocalMachine = false)
|
||||
{
|
||||
Debug.Assert(secret != null);
|
||||
|
|
@ -35,7 +51,7 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
||||
fixed (byte* pbPurpose = _purpose)
|
||||
{
|
||||
return ProtectWithDpapiImpl(pbPlaintextSecret, (uint)plaintextSecret.Length, pbPurpose, (uint)_purpose.Length, fLocalMachine: protectToLocalMachine);
|
||||
return ProtectWithDpapiCore(pbPlaintextSecret, (uint)plaintextSecret.Length, pbPurpose, (uint)_purpose.Length, fLocalMachine: protectToLocalMachine);
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
|
@ -46,7 +62,7 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
}
|
||||
}
|
||||
|
||||
internal static byte[] ProtectWithDpapiImpl(byte* pbSecret, uint cbSecret, byte* pbOptionalEntropy, uint cbOptionalEntropy, bool fLocalMachine = false)
|
||||
internal static byte[] ProtectWithDpapiCore(byte* pbSecret, uint cbSecret, byte* pbOptionalEntropy, uint cbOptionalEntropy, bool fLocalMachine = false)
|
||||
{
|
||||
byte dummy; // provides a valid memory address if the secret or entropy has zero length
|
||||
|
||||
|
|
@ -110,7 +126,7 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
||||
|
||||
byte dummy; // used to provide a valid memory address if secret is zero-length
|
||||
return ProtectWithDpapiNGImpl(
|
||||
return ProtectWithDpapiNGCore(
|
||||
protectionDescriptorHandle: protectionDescriptorHandle,
|
||||
pbData: (pbPlaintextSecret != null) ? pbPlaintextSecret : &dummy,
|
||||
cbData: (uint)plaintextSecret.Length);
|
||||
|
|
@ -123,7 +139,7 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
}
|
||||
}
|
||||
|
||||
private static byte[] ProtectWithDpapiNGImpl(NCryptDescriptorHandle protectionDescriptorHandle, byte* pbData, uint cbData)
|
||||
private static byte[] ProtectWithDpapiNGCore(NCryptDescriptorHandle protectionDescriptorHandle, byte* pbData, uint cbData)
|
||||
{
|
||||
Debug.Assert(protectionDescriptorHandle != null);
|
||||
Debug.Assert(pbData != null);
|
||||
|
|
@ -141,7 +157,7 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
ppbProtectedBlob: out protectedData,
|
||||
pcbProtectedBlob: out cbProtectedData);
|
||||
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
||||
CryptoUtil.Assert(protectedData != null && !protectedData.IsInvalid, "protectedData != null && !protectedData.IsInvalid");
|
||||
CryptoUtil.AssertSafeHandleIsValid(protectedData);
|
||||
|
||||
// Copy the data from LocalAlloc-allocated memory into a managed memory buffer.
|
||||
using (protectedData)
|
||||
|
|
@ -181,12 +197,12 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
{
|
||||
fixed (byte* pbPurpose = _purpose)
|
||||
{
|
||||
return UnprotectWithDpapiImpl(pbProtectedSecret, (uint)protectedSecret.Length, pbPurpose, (uint)_purpose.Length);
|
||||
return UnprotectWithDpapiCore(pbProtectedSecret, (uint)protectedSecret.Length, pbPurpose, (uint)_purpose.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static Secret UnprotectWithDpapiImpl(byte* pbProtectedData, uint cbProtectedData, byte* pbOptionalEntropy, uint cbOptionalEntropy)
|
||||
internal static Secret UnprotectWithDpapiCore(byte* pbProtectedData, uint cbProtectedData, byte* pbOptionalEntropy, uint cbOptionalEntropy)
|
||||
{
|
||||
byte dummy; // provides a valid memory address if the secret or entropy has zero length
|
||||
|
||||
|
|
@ -242,13 +258,13 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
fixed (byte* pbProtectedData = protectedData)
|
||||
{
|
||||
byte dummy; // used to provide a valid memory address if protected data is zero-length
|
||||
return UnprotectWithDpapiNGImpl(
|
||||
return UnprotectWithDpapiNGCore(
|
||||
pbData: (pbProtectedData != null) ? pbProtectedData : &dummy,
|
||||
cbData: (uint)protectedData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private static Secret UnprotectWithDpapiNGImpl(byte* pbData, uint cbData)
|
||||
private static Secret UnprotectWithDpapiNGCore(byte* pbData, uint cbData)
|
||||
{
|
||||
Debug.Assert(pbData != null);
|
||||
|
||||
|
|
@ -265,7 +281,7 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
ppbData: out unencryptedPayloadHandle,
|
||||
pcbData: out cbUnencryptedPayload);
|
||||
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
||||
CryptoUtil.Assert(unencryptedPayloadHandle != null && !unencryptedPayloadHandle.IsInvalid, "unencryptedPayloadHandle != null && !unencryptedPayloadHandle.IsInvalid");
|
||||
CryptoUtil.AssertSafeHandleIsValid(unencryptedPayloadHandle);
|
||||
|
||||
// Copy the data from LocalAlloc-allocated memory into a CryptProtectMemory-protected buffer.
|
||||
// There's a small window between NCryptUnprotectSecret returning and the call to PrepareConstrainedRegions
|
||||
|
|
@ -293,5 +309,50 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetRuleFromDpapiNGProtectedPayload(byte[] protectedData)
|
||||
{
|
||||
Debug.Assert(protectedData != null);
|
||||
|
||||
fixed (byte* pbProtectedData = protectedData)
|
||||
{
|
||||
byte dummy; // used to provide a valid memory address if protected data is zero-length
|
||||
return GetRuleFromDpapiNGProtectedPayloadCore(
|
||||
pbData: (pbProtectedData != null) ? pbProtectedData : &dummy,
|
||||
cbData: (uint)protectedData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRuleFromDpapiNGProtectedPayloadCore(byte* pbData, uint cbData)
|
||||
{
|
||||
// from ncryptprotect.h
|
||||
const uint NCRYPT_UNPROTECT_NO_DECRYPT = 0x00000001;
|
||||
|
||||
NCryptDescriptorHandle descriptorHandle;
|
||||
LocalAllocHandle unprotectedDataHandle;
|
||||
uint cbUnprotectedData;
|
||||
int ntstatus = UnsafeNativeMethods.NCryptUnprotectSecret(
|
||||
phDescriptor: out descriptorHandle,
|
||||
dwFlags: NCRYPT_UNPROTECT_NO_DECRYPT,
|
||||
pbProtectedBlob: pbData,
|
||||
cbProtectedBlob: cbData,
|
||||
pMemPara: IntPtr.Zero,
|
||||
hWnd: IntPtr.Zero,
|
||||
ppbData: out unprotectedDataHandle,
|
||||
pcbData: out cbUnprotectedData);
|
||||
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
||||
CryptoUtil.AssertSafeHandleIsValid(descriptorHandle);
|
||||
|
||||
if (unprotectedDataHandle != null && !unprotectedDataHandle.IsInvalid)
|
||||
{
|
||||
// we don't care about this value
|
||||
unprotectedDataHandle.Dispose();
|
||||
}
|
||||
|
||||
using (descriptorHandle)
|
||||
{
|
||||
return descriptorHandle.GetProtectionDescriptorRuleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using Microsoft.AspNet.Cryptography;
|
||||
using Microsoft.AspNet.Cryptography.Cng;
|
||||
using Microsoft.AspNet.Cryptography.SafeHandles;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNet.DataProtection.SP800_108;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection.Cng
|
||||
|
|
@ -38,9 +39,10 @@ namespace Microsoft.AspNet.DataProtection.Cng
|
|||
|
||||
public GcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom genRandom = null)
|
||||
{
|
||||
CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
|
||||
"KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");
|
||||
|
||||
// Is the key size appropriate?
|
||||
AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked(symmetricAlgorithmKeySizeInBytes * 8));
|
||||
CryptoUtil.Assert(symmetricAlgorithmHandle.GetCipherBlockLength() == 128 / 8, "GCM requires a block cipher algorithm with a 128-bit block size.");
|
||||
|
||||
_genRandom = genRandom ?? BCryptGenRandomImpl.Instance;
|
||||
_sp800_108_ctr_hmac_provider = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey);
|
||||
_symmetricAlgorithmHandle = symmetricAlgorithmHandle;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,372 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using Microsoft.AspNet.DataProtection.AuthenticatedEncryption;
|
||||
using Microsoft.AspNet.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNet.DataProtection.XmlEncryption;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Win32;
|
||||
|
||||
#if !DNXCORE50 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to configuration for the data protection system, which allows the
|
||||
/// developer to configure default cryptographic algorithms, key storage locations,
|
||||
/// and the mechanism by which keys are protected at rest.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If the developer changes the at-rest key protection mechanism, it is intended that
|
||||
/// he also change the key storage location, and vice versa. For instance, a call to
|
||||
/// <see cref="ProtectKeysWithCertificate(string)"/> should generally be accompanied by
|
||||
/// a call to <see cref="PersistKeysToFileSystem(DirectoryInfo)"/>, or exceptions may
|
||||
/// occur at runtime due to the data protection system not knowing where to persist keys.
|
||||
/// </para>
|
||||
/// <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="UseCryptographicAlgorithms(AuthenticatedEncryptionOptions)"/>
|
||||
/// should therefore generally be paired with a call to <see cref="PersistKeysToFileSystem(DirectoryInfo)"/>,
|
||||
/// for example.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// When the default cryptographic algorithms or at-rest key protection mechanisms are
|
||||
/// changed, they only affect <strong>new</strong> keys in the repository. The repository may
|
||||
/// contain existing keys that use older algorithms or protection mechanisms.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class DataProtectionConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new configuration object linked to a <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
public DataProtectionConfiguration([NotNull] IServiceCollection services)
|
||||
{
|
||||
Services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the <see cref="IServiceCollection"/> passed to this object's constructor.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IServiceCollection Services { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IKeyEscrowSink"/> to perform escrow before keys are persisted to storage.
|
||||
/// </summary>
|
||||
/// <param name="sink">The instance of the <see cref="IKeyEscrowSink"/> to register.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// Registrations are additive.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration AddKeyEscrowSink([NotNull] IKeyEscrowSink sink)
|
||||
{
|
||||
Services.AddInstance<IKeyEscrowSink>(sink);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IKeyEscrowSink"/> to perform escrow before keys are persisted to storage.
|
||||
/// </summary>
|
||||
/// <typeparam name="TImplementation">The concrete type of the <see cref="IKeyEscrowSink"/> to register.</typeparam>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// Registrations are additive.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration AddKeyEscrowSink<TImplementation>()
|
||||
where TImplementation : IKeyEscrowSink
|
||||
{
|
||||
Services.AddSingleton<IKeyEscrowSink, TImplementation>();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="IKeyEscrowSink"/> to perform escrow before keys are persisted to storage.
|
||||
/// </summary>
|
||||
/// <param name="factory">A factory that creates the <see cref="IKeyEscrowSink"/> instance.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// Registrations are additive.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration AddKeyEscrowSink([NotNull] Func<IServiceProvider, IKeyEscrowSink> factory)
|
||||
{
|
||||
Services.AddSingleton<IKeyEscrowSink>(factory);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures miscellaneous global options.
|
||||
/// </summary>
|
||||
/// <param name="setupAction">A callback that configures the global options.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration ConfigureGlobalOptions([NotNull] Action<DataProtectionOptions> setupAction)
|
||||
{
|
||||
Services.Configure(setupAction);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to persist keys to the specified directory.
|
||||
/// This path may be on the local machine or may point to a UNC share.
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory in which to store keys.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration PersistKeysToFileSystem([NotNull] DirectoryInfo directory)
|
||||
{
|
||||
Use(DataProtectionServiceDescriptors.IXmlRepository_FileSystem(directory));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to persist keys to the Windows registry.
|
||||
/// </summary>
|
||||
/// <param name="registryKey">The location in the registry where keys should be stored.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration PersistKeysToRegistry([NotNull] RegistryKey registryKey)
|
||||
{
|
||||
Use(DataProtectionServiceDescriptors.IXmlRepository_Registry(registryKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
#if !DNXCORE50 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||
|
||||
/// <summary>
|
||||
/// Configures keys to be encrypted to a given certificate before being persisted to storage.
|
||||
/// </summary>
|
||||
/// <param name="certificate">The certificate to use when encrypting keys.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration ProtectKeysWithCertificate([NotNull] X509Certificate2 certificate)
|
||||
{
|
||||
Use(DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(certificate));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures keys to be encrypted to a given certificate before being persisted to storage.
|
||||
/// </summary>
|
||||
/// <param name="thumbprint">The thumbprint of the certificate to use when encrypting keys.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration ProtectKeysWithCertificate([NotNull] string thumbprint)
|
||||
{
|
||||
// Make sure the thumbprint corresponds to a valid certificate.
|
||||
if (new CertificateResolver().ResolveCertificate(thumbprint) == null)
|
||||
{
|
||||
throw Error.CertificateXmlEncryptor_CertificateNotFound(thumbprint);
|
||||
}
|
||||
|
||||
// ICertificateResolver is necessary for this type to work correctly, so register it
|
||||
// if it doesn't already exist.
|
||||
Services.TryAdd(DataProtectionServiceDescriptors.ICertificateResolver_Default());
|
||||
Use(DataProtectionServiceDescriptors.IXmlEncryptor_Certificate(thumbprint));
|
||||
return this;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Configures keys to be encrypted with Windows DPAPI before being persisted to
|
||||
/// storage. The encrypted key will only be decryptable by the current Windows user account.
|
||||
/// </summary>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// This API is only supported on Windows platforms.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration ProtectKeysWithDpapi()
|
||||
{
|
||||
return ProtectKeysWithDpapi(protectToLocalMachine: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures keys to be encrypted with Windows DPAPI before being persisted to
|
||||
/// storage.
|
||||
/// </summary>
|
||||
/// <param name="protectToLocalMachine">'true' if the key should be decryptable by any
|
||||
/// use on the local machine, 'false' if the key should only be decryptable by the current
|
||||
/// Windows user account.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// This API is only supported on Windows platforms.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration ProtectKeysWithDpapi(bool protectToLocalMachine)
|
||||
{
|
||||
Use(DataProtectionServiceDescriptors.IXmlEncryptor_Dpapi(protectToLocalMachine));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures keys to be encrypted with Windows CNG DPAPI before being persisted
|
||||
/// to storage. The keys will be decryptable by the current Windows user account.
|
||||
/// </summary>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/hh706794(v=vs.85).aspx
|
||||
/// for more information on DPAPI-NG. This API is only supported on Windows 8 / Windows Server 2012 and higher.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration ProtectKeysWithDpapiNG()
|
||||
{
|
||||
return ProtectKeysWithDpapiNG(
|
||||
protectionDescriptorRule: DpapiNGXmlEncryptor.GetDefaultProtectionDescriptorString(),
|
||||
flags: DpapiNGProtectionDescriptorFlags.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures keys to be encrypted with Windows CNG DPAPI before being persisted to storage.
|
||||
/// </summary>
|
||||
/// <param name="protectionDescriptorRule">The descriptor rule string with which to protect the key material.</param>
|
||||
/// <param name="flags">Flags that should be passed to the call to 'NCryptCreateProtectionDescriptor'.
|
||||
/// The default value of this parameter is <see cref="DpapiNGProtectionDescriptorFlags.None"/>.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/hh769091(v=vs.85).aspx
|
||||
/// and https://msdn.microsoft.com/en-us/library/windows/desktop/hh706800(v=vs.85).aspx
|
||||
/// for more information on valid values for the the <paramref name="descriptor"/>
|
||||
/// and <paramref name="flags"/> arguments.
|
||||
/// This API is only supported on Windows 8 / Windows Server 2012 and higher.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration ProtectKeysWithDpapiNG([NotNull] string protectionDescriptorRule, DpapiNGProtectionDescriptorFlags flags)
|
||||
{
|
||||
Use(DataProtectionServiceDescriptors.IXmlEncryptor_DpapiNG(protectionDescriptorRule, flags));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default lifetime of keys created by the data protection system.
|
||||
/// </summary>
|
||||
/// <param name="lifetime">The lifetime (time before expiration) for newly-created keys.
|
||||
/// See <see cref="KeyLifetimeOptions.NewKeyLifetime"/> for more information and
|
||||
/// usage notes.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration SetDefaultKeyLifetime(TimeSpan lifetime)
|
||||
{
|
||||
Services.Configure<KeyLifetimeOptions>(options =>
|
||||
{
|
||||
options.NewKeyLifetime = lifetime;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to persist keys in storage as plaintext.
|
||||
/// </summary>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// Caution: cryptographic key material will not be protected at rest.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration SuppressProtectionOfKeysAtRest()
|
||||
{
|
||||
RemoveAllServicesOfType(typeof(IXmlEncryptor));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to use the specified cryptographic algorithms
|
||||
/// by default when generating protected payloads.
|
||||
/// </summary>
|
||||
/// <param name="options">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
public DataProtectionConfiguration UseCryptographicAlgorithms([NotNull] AuthenticatedEncryptionOptions options)
|
||||
{
|
||||
return UseCryptographicAlgorithmsCore(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to use custom Windows CNG algorithms.
|
||||
/// This API is intended for advanced scenarios where the developer cannot use the
|
||||
/// algorithms specified in the <see cref="EncryptionAlgorithm"/> and
|
||||
/// <see cref="ValidationAlgorithm"/> enumerations.
|
||||
/// </summary>
|
||||
/// <param name="options">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// This API is only available on Windows.
|
||||
/// </remarks>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public DataProtectionConfiguration UseCustomCryptographicAlgorithms([NotNull] CngCbcAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
return UseCryptographicAlgorithmsCore(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to use custom Windows CNG algorithms.
|
||||
/// This API is intended for advanced scenarios where the developer cannot use the
|
||||
/// algorithms specified in the <see cref="EncryptionAlgorithm"/> and
|
||||
/// <see cref="ValidationAlgorithm"/> enumerations.
|
||||
/// </summary>
|
||||
/// <param name="options">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// This API is only available on Windows.
|
||||
/// </remarks>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public DataProtectionConfiguration UseCustomCryptographicAlgorithms([NotNull] CngGcmAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
return UseCryptographicAlgorithmsCore(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to use custom algorithms.
|
||||
/// This API is intended for advanced scenarios where the developer cannot use the
|
||||
/// algorithms specified in the <see cref="EncryptionAlgorithm"/> and
|
||||
/// <see cref="ValidationAlgorithm"/> enumerations.
|
||||
/// </summary>
|
||||
/// <param name="options">Information about what cryptographic algorithms should be used.</param>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public DataProtectionConfiguration UseCustomCryptographicAlgorithms([NotNull] ManagedAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
return UseCryptographicAlgorithmsCore(options);
|
||||
}
|
||||
|
||||
private DataProtectionConfiguration UseCryptographicAlgorithmsCore(IInternalAuthenticatedEncryptionOptions options)
|
||||
{
|
||||
options.Validate(); // perform self-test
|
||||
Use(DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromOptions(options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the data protection system to use the <see cref="EphemeralDataProtectionProvider"/>
|
||||
/// for data protection services.
|
||||
/// </summary>
|
||||
/// <returns>The 'this' instance.</returns>
|
||||
/// <remarks>
|
||||
/// If this option is used, payloads protected by the data protection system will
|
||||
/// be permanently undecipherable after the application exits.
|
||||
/// </remarks>
|
||||
public DataProtectionConfiguration UseEphemeralDataProtectionProvider()
|
||||
{
|
||||
Use(DataProtectionServiceDescriptors.IDataProtectionProvider_Ephemeral());
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* UTILITY ISERVICECOLLECTION METHODS
|
||||
*/
|
||||
|
||||
private void RemoveAllServicesOfType(Type serviceType)
|
||||
{
|
||||
// We go backward since we're modifying the collection in-place.
|
||||
for (int i = Services.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (Services[i]?.ServiceType == serviceType)
|
||||
{
|
||||
Services.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Use(ServiceDescriptor descriptor)
|
||||
{
|
||||
RemoveAllServicesOfType(descriptor.ServiceType);
|
||||
Services.Add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNet.Cryptography;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
|
|
@ -22,81 +21,5 @@ namespace Microsoft.AspNet.DataProtection
|
|||
return (protector as ITimeLimitedDataProtector)
|
||||
?? new TimeLimitedDataProtector(protector.CreateProtector(TimeLimitedDataProtector.PurposeString));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an IDataProtector given an array of purposes.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider from which to generate the purpose chain.</param>
|
||||
/// <param name="purposes">
|
||||
/// This is a convenience method used for chaining several purposes together
|
||||
/// in a single call to CreateProtector. See the documentation of
|
||||
/// IDataProtectionProvider.CreateProtector for more information.
|
||||
/// </param>
|
||||
/// <returns>An IDataProtector tied to the provided purpose chain.</returns>
|
||||
public static IDataProtector CreateProtector([NotNull] this IDataProtectionProvider provider, params string[] purposes)
|
||||
{
|
||||
if (purposes == null || purposes.Length == 0)
|
||||
{
|
||||
throw new ArgumentException(Resources.DataProtectionExtensions_NullPurposesArray, nameof(purposes));
|
||||
}
|
||||
|
||||
IDataProtectionProvider retVal = provider;
|
||||
foreach (string purpose in purposes)
|
||||
{
|
||||
if (String.IsNullOrEmpty(purpose))
|
||||
{
|
||||
throw new ArgumentException(Resources.DataProtectionExtensions_NullPurposesArray, nameof(purposes));
|
||||
}
|
||||
retVal = retVal.CreateProtector(purpose) ?? CryptoUtil.Fail<IDataProtector>("CreateProtector returned null.");
|
||||
}
|
||||
|
||||
Debug.Assert(retVal is IDataProtector); // CreateProtector is supposed to return an instance of this interface
|
||||
return (IDataProtector)retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cryptographically protects a piece of plaintext data.
|
||||
/// </summary>
|
||||
/// <param name="protector">The data protector to use for this operation.</param>
|
||||
/// <param name="unprotectedData">The plaintext data to protect.</param>
|
||||
/// <returns>The protected form of the plaintext data.</returns>
|
||||
public static string Protect([NotNull] this IDataProtector protector, [NotNull] string unprotectedData)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] unprotectedDataAsBytes = EncodingUtil.SecureUtf8Encoding.GetBytes(unprotectedData);
|
||||
byte[] protectedDataAsBytes = protector.Protect(unprotectedDataAsBytes);
|
||||
return WebEncoders.Base64UrlEncode(protectedDataAsBytes);
|
||||
}
|
||||
catch (Exception ex) when (ex.RequiresHomogenization())
|
||||
{
|
||||
// Homogenize exceptions to CryptographicException
|
||||
throw Error.CryptCommon_GenericError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cryptographically unprotects a piece of protected data.
|
||||
/// </summary>
|
||||
/// <param name="protector">The data protector to use for this operation.</param>
|
||||
/// <param name="protectedData">The protected data to unprotect.</param>
|
||||
/// <returns>The plaintext form of the protected data.</returns>
|
||||
/// <remarks>
|
||||
/// This method will throw CryptographicException if the input is invalid or malformed.
|
||||
/// </remarks>
|
||||
public static string Unprotect([NotNull] this IDataProtector protector, [NotNull] string protectedData)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] protectedDataAsBytes = WebEncoders.Base64UrlDecode(protectedData);
|
||||
byte[] unprotectedDataAsBytes = protector.Unprotect(protectedDataAsBytes);
|
||||
return EncodingUtil.SecureUtf8Encoding.GetString(unprotectedDataAsBytes);
|
||||
}
|
||||
catch (Exception ex) when (ex.RequiresHomogenization())
|
||||
{
|
||||
// Homogenize exceptions to CryptographicException
|
||||
throw Error.CryptCommon_GenericError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,21 @@ using System;
|
|||
|
||||
namespace Microsoft.AspNet.DataProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides global options for the Data Protection system.
|
||||
/// </summary>
|
||||
public class DataProtectionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// An identifier that uniquely discriminates this application from all other
|
||||
/// applications on the machine. The discriminator value is implicitly included
|
||||
/// in all protected payloads generated by the data protection system to isolate
|
||||
/// multiple logical applications that all happen to be using the same key material.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If two different applications need to share protected payloads, they should
|
||||
/// ensure that this property is set to the same value across both applications.
|
||||
/// </remarks>
|
||||
public string ApplicationDiscriminator { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue