diff --git a/DataProtection.sln b/DataProtection.sln
index b98012a3..71fd7cbe 100644
--- a/DataProtection.sln
+++ b/DataProtection.sln
@@ -52,6 +52,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.DataPr
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Redis", "samples\Redis\Redis.xproj", "{24AAEC96-DF46-4F61-B2FF-3D5E056685D9}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.DataProtection.DistributedStore", "src\Microsoft.AspNetCore.DataProtection.DistributedStore\Microsoft.AspNetCore.DataProtection.DistributedStore.xproj", "{5694BC38-B266-447E-A743-DA1139FCB257}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.DataProtection.DistributedStore.Test", "test\Microsoft.AspNetCore.DataProtection.DistributedStore.Test\Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj", "{FFD8D535-91EB-405F-AB80-8C2D972D5C66}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DistributedStore", "samples\DistributedStore\DistributedStore.xproj", "{CC657C4D-D281-4CB3-86E9-053D68817218}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -232,5 +238,8 @@ Global
{ABCF00E5-5B2F-469C-90DC-908C5A04C08D} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{8C41240E-48F8-402F-9388-74CFE27F4D76} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
{24AAEC96-DF46-4F61-B2FF-3D5E056685D9} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
+ {5694BC38-B266-447E-A743-DA1139FCB257} = {5FCB2DA3-5395-47F5-BCEE-E0EA319448EA}
+ {FFD8D535-91EB-405F-AB80-8C2D972D5C66} = {60336AB3-948D-4D15-A5FB-F32A2B91E814}
+ {CC657C4D-D281-4CB3-86E9-053D68817218} = {5A3A5DE3-49AD-431C-971D-B01B62D94AE2}
EndGlobalSection
EndGlobal
diff --git a/samples/DistributedStore/DistributedStore.xproj b/samples/DistributedStore/DistributedStore.xproj
new file mode 100644
index 00000000..24fb1f54
--- /dev/null
+++ b/samples/DistributedStore/DistributedStore.xproj
@@ -0,0 +1,18 @@
+
+
+
+ 14.0.25420
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ cc657c4d-d281-4cb3-86e9-053d68817218
+ DistributedStore
+ .\obj
+ .\bin\
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/samples/DistributedStore/Program.cs b/samples/DistributedStore/Program.cs
new file mode 100644
index 00000000..c549c80c
--- /dev/null
+++ b/samples/DistributedStore/Program.cs
@@ -0,0 +1,29 @@
+using System;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace DistributedStore
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddLogging();
+ serviceCollection.AddDistributedMemoryCache();
+ serviceCollection.AddDataProtection()
+ .PersistKeysToDistributedStore();
+
+ var services = serviceCollection.BuildServiceProvider();
+ var loggerFactory = services.GetService();
+ loggerFactory.AddConsole(LogLevel.Trace);
+
+ var protector = services.GetDataProtector("sample-purpose");
+ var protectedData = protector.Protect("Hello world!");
+ Console.WriteLine($"{nameof(protectedData)}: {protectedData}");
+ var unprotectedData = protector.Unprotect(protectedData);
+ Console.WriteLine($"{nameof(unprotectedData)}: {unprotectedData}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/DistributedStore/project.json b/samples/DistributedStore/project.json
new file mode 100644
index 00000000..6db5a228
--- /dev/null
+++ b/samples/DistributedStore/project.json
@@ -0,0 +1,25 @@
+{
+ "version": "1.0.0-*",
+ "buildOptions": {
+ "debugType": "portable",
+ "emitEntryPoint": true
+ },
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "1.2.0-*",
+ "Microsoft.Extensions.Logging": "1.2.0-*",
+ "Microsoft.Extensions.Logging.Console": "1.2.0-*",
+ "Microsoft.Extensions.Caching.Memory": "1.2.0-*",
+ "Microsoft.AspNetCore.DataProtection.DistributedStore": "1.0.0-*"
+ },
+ "frameworks": {
+ "netcoreapp1.1": {
+ "dependencies": {
+ "Microsoft.NETCore.App": {
+ "version": "1.2.0-*",
+ "type": "platform"
+ }
+ }
+ },
+ "net451": {}
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreDataProtectionBuilderExtensions.cs b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreDataProtectionBuilderExtensions.cs
new file mode 100644
index 00000000..3bd13d74
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreDataProtectionBuilderExtensions.cs
@@ -0,0 +1,26 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.DataProtection.Repositories;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Caching.Distributed;
+
+namespace Microsoft.AspNetCore.DataProtection
+{
+ public static class DistributedStoreDataProtectionBuilderExtensions
+ {
+ public static IDataProtectionBuilder PersistKeysToDistributedStore(this IDataProtectionBuilder builder)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.Services.AddSingleton();
+
+ return builder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreXmlRepository.cs b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreXmlRepository.cs
new file mode 100644
index 00000000..2b438baf
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/DistributedStoreXmlRepository.cs
@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+using Microsoft.AspNetCore.DataProtection.Repositories;
+using Microsoft.Extensions.Caching.Distributed;
+using System.Text;
+
+namespace Microsoft.AspNetCore.DataProtection
+{
+ public class DistributedStoreXmlRepository: IXmlRepository
+ {
+ private readonly IDistributedCache _cache;
+ private readonly string _key;
+
+ public DistributedStoreXmlRepository(
+ IDistributedCache cache,
+ string key = "DataProtection-Keys")
+ {
+ if (cache == null)
+ {
+ throw new ArgumentNullException(nameof(cache));
+ }
+ _cache = cache;
+
+ _key = key;
+ }
+
+ public IReadOnlyCollection GetAllElements()
+ {
+ var data = _cache.GetString(_key);
+ if(!string.IsNullOrEmpty(data))
+ {
+ return XDocument.Parse(data).Root.Elements().ToList().AsReadOnly();
+ }
+ else
+ {
+ return new List().AsReadOnly();
+ }
+ }
+
+ public void StoreElement(XElement element, string friendlyName)
+ {
+ var data = _cache.GetString(_key);
+
+ XDocument doc = string.IsNullOrEmpty(data) ?
+ new XDocument(new XElement("keys")) : XDocument.Parse(data);
+ doc.Root.Add(element);
+
+ _cache.SetString(_key, doc.ToString());
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Microsoft.AspNetCore.DataProtection.DistributedStore.xproj b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Microsoft.AspNetCore.DataProtection.DistributedStore.xproj
new file mode 100644
index 00000000..533bdac8
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Microsoft.AspNetCore.DataProtection.DistributedStore.xproj
@@ -0,0 +1,18 @@
+
+
+
+ 14.0.25420
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 5694bc38-b266-447e-a743-da1139fcb257
+ Microsoft.AspNetCore.DataProtection.DistributedStore
+ .\obj
+ .\bin\
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..e3ae91c5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Reflection;
+using System.Resources;
+
+[assembly: AssemblyMetadata("Serviceable", "True")]
+[assembly: NeutralResourcesLanguage("en-US")]
+[assembly: AssemblyCompany("Microsoft Corporation.")]
+[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
+[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
diff --git a/src/Microsoft.AspNetCore.DataProtection.DistributedStore/project.json b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/project.json
new file mode 100644
index 00000000..6cf91eda
--- /dev/null
+++ b/src/Microsoft.AspNetCore.DataProtection.DistributedStore/project.json
@@ -0,0 +1,34 @@
+{
+ "version": "1.0.0-*",
+ "description": "IDistributedCache as key store.",
+ "packOptions": {
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aspnet/dataprotection"
+ },
+ "tags": [
+ "aspnetcore",
+ "dataprotection"
+ ]
+ },
+ "dependencies": {
+ "Microsoft.AspNetCore.DataProtection": {
+ "target": "project"
+ },
+ "Microsoft.Extensions.Caching.Abstractions": "1.2.0-*",
+ "NETStandard.Library": "1.6.2-*"
+ },
+ "frameworks": {
+ "net451": {},
+ "netstandard1.5": {}
+ },
+ "buildOptions": {
+ "allowUnsafe": true,
+ "warningsAsErrors": true,
+ "keyFile": "../../tools/Key.snk",
+ "nowarn": [
+ "CS1591"
+ ],
+ "xmlDoc": true
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/DataProtectionDistributedStoreTests.cs b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/DataProtectionDistributedStoreTests.cs
new file mode 100644
index 00000000..7edbdf25
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/DataProtectionDistributedStoreTests.cs
@@ -0,0 +1,35 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Extensions.Caching.Distributed;
+using Xunit;
+using Microsoft.Extensions.Caching.Memory;
+
+namespace Microsoft.AspNetCore.DataProtection
+{
+ public class DataProtectionDistributedStoreTests
+ {
+ private DistributedStoreXmlRepository _xmlRepository;
+
+ public DataProtectionDistributedStoreTests()
+ {
+ _xmlRepository = new DistributedStoreXmlRepository(
+ new MemoryDistributedCache(new MemoryCache(new MemoryCacheOptions()))); ;
+ }
+
+ [Fact]
+ public void StoreElement_GetAllElements()
+ {
+ var element1 = XElement.Parse("");
+ var element2 = XElement.Parse("");
+ _xmlRepository.StoreElement(element1, null);
+ _xmlRepository.StoreElement(element2, null);
+ var actual = _xmlRepository.GetAllElements().Select(e => e.ToString());
+ var expected = new XElement[] { element1, element2 }.Select(e => e.ToString());
+ Assert.True(expected.SequenceEqual(actual));
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj
new file mode 100644
index 00000000..b5a153cb
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0.25420
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ ffd8d535-91eb-405f-ab80-8c2d972d5c66
+ Microsoft.AspNetCore.DataProtection.DistributedStore.Test
+ .\obj
+ .\bin\
+
+
+ 2.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/project.json b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/project.json
new file mode 100644
index 00000000..39250bdd
--- /dev/null
+++ b/test/Microsoft.AspNetCore.DataProtection.DistributedStore.Test/project.json
@@ -0,0 +1,29 @@
+{
+ "dependencies": {
+ "dotnet-test-xunit": "2.2.0-*",
+ "Microsoft.AspNetCore.DataProtection.Abstractions": "1.2.0-*",
+ "Microsoft.AspNetCore.DataProtection.DistributedStore": "1.0.0-*",
+ "Microsoft.AspNetCore.Testing": "1.2.0-*",
+ "Microsoft.Extensions.Caching.Memory": "1.2.0-*",
+ "xunit": "2.2.0-*"
+ },
+ "frameworks": {
+ "netcoreapp1.1": {
+ "dependencies": {
+ "Microsoft.NETCore.App": {
+ "version": "1.2.0-*",
+ "type": "platform"
+ }
+ }
+ },
+ "net451": {}
+ },
+ "testRunner": "xunit",
+ "buildOptions": {
+ "warningsAsErrors": true,
+ "keyFile": "../../tools/Key.snk",
+ "compile": {
+ "include": "../common/**/*.cs"
+ }
+ }
+}
\ No newline at end of file