-
-
Save guardrex/7342544cab59cd9cbf3a22f0104b8fdd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// FROM Program.cs Program > Main | |
services.Configure<CustomDataProtection.DataProtectionOptions>(options => | |
{ | |
options.StorageAccount = storageAccountName; | |
options.StorageKey = storageAccountKey; | |
options.TableName = dataProtectionTable; | |
options.Key = new byte[]{ KEY IS HERE ... LOAD FROM AZURE KEY VAULT }; | |
}); | |
services.AddSingleton<IXmlRepository, CustomXmlRepository>(); | |
services.AddSingleton<IXmlEncryptor, CustomXmlEncryptor>(); | |
services.AddSingleton<IXmlDecryptor, CustomXmlDecryptor>(); | |
// FROM DataProtectionKey.cs | |
using System; | |
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using Microsoft.WindowsAzure.Storage; | |
using Microsoft.WindowsAzure.Storage.Auth; | |
using Microsoft.WindowsAzure.Storage.RetryPolicies; | |
using Microsoft.WindowsAzure.Storage.Table; | |
namespace CustomDataProtection | |
{ | |
public class DataProtectionKey : TableEntity | |
{ | |
public DataProtectionKey() | |
{ | |
} | |
public DataProtectionKey(string ts) | |
{ | |
PartitionKey = "Key"; | |
RowKey = ts; | |
} | |
public string Data { get; set; } | |
public static async Task<IEnumerable<DataProtectionKey>> FetchAll(string storageAccount, string storageKey, string tableName) | |
{ | |
StorageCredentials storageCredentials = new StorageCredentials(storageAccount, storageKey); | |
CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(storageCredentials, true); | |
CloudTableClient tableClient = cloudStorageAccount.CreateCloudTableClient(); | |
tableClient.DefaultRequestOptions.RetryPolicy = new ExponentialRetry(TimeSpan.FromSeconds(1), 10); | |
CloudTable table_DataProtectionKey = tableClient.GetTableReference(tableName); | |
List<DataProtectionKey> dataProtectionKeys = new List<DataProtectionKey>(); | |
TableContinuationToken tableContinuationToken = null; | |
List<string> deleteKeys = new List<string>(); | |
do | |
{ | |
var queryResponse = await table_DataProtectionKey.ExecuteQuerySegmentedAsync(new TableQuery<DataProtectionKey>(), tableContinuationToken, null, null); | |
foreach (var key in queryResponse.Results) | |
{ | |
if (string.CompareOrdinal(DateTime.UtcNow.ToString("s"), key.RowKey) > 0) | |
{ | |
deleteKeys.Add(key.RowKey); | |
} | |
else | |
{ | |
dataProtectionKeys.Add(key); | |
} | |
} | |
tableContinuationToken = queryResponse.ContinuationToken; | |
} | |
while (tableContinuationToken != null); | |
foreach (var key in deleteKeys) | |
{ | |
await Remove(key, storageAccount, storageKey, tableName); | |
} | |
return dataProtectionKeys; | |
} | |
public static async Task<bool> Remove(string ts, string storageAccount, string storageKey, string tableName) | |
{ | |
StorageCredentials storageCredentials = new StorageCredentials(storageAccount, storageKey); | |
CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(storageCredentials, true); | |
CloudTableClient tableClient = cloudStorageAccount.CreateCloudTableClient(); | |
tableClient.DefaultRequestOptions.RetryPolicy = new ExponentialRetry(TimeSpan.FromSeconds(1), 10); | |
CloudTable table_DataProtectionKey = tableClient.GetTableReference(tableName); | |
DataProtectionKey entity = new DataProtectionKey(ts); | |
entity.ETag = "*"; | |
TableResult operationResult = await table_DataProtectionKey.ExecuteAsync(TableOperation.Delete(entity)); | |
return operationResult.HttpStatusCode == 204 ? true : false; | |
} | |
public async static Task<bool> Insert(string data, string storageAccount, string storageKey, string tableName) | |
{ | |
StorageCredentials storageCredentials = new StorageCredentials(storageAccount, storageKey); | |
CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(storageCredentials, true); | |
CloudTableClient tableClient = cloudStorageAccount.CreateCloudTableClient(); | |
tableClient.DefaultRequestOptions.RetryPolicy = new ExponentialRetry(TimeSpan.FromSeconds(1), 10); | |
CloudTable table_DataProtectionKey = tableClient.GetTableReference(tableName); | |
DataProtectionKey entity = new DataProtectionKey(DateTime.UtcNow.AddMinutes(1441).ToString("s")); | |
entity.Data = data; | |
var operationResult = await table_DataProtectionKey.ExecuteAsync(TableOperation.Insert(entity)); | |
return operationResult.HttpStatusCode == 201 ? true : false; | |
} | |
} | |
} | |
// FROM DataProtectionOptions.cs | |
namespace CustomDataProtection | |
{ | |
public class DataProtectionOptions | |
{ | |
public DataProtectionOptions() | |
{ | |
} | |
public byte[] Key { get; set; } | |
public string StorageAccount; | |
public string StorageKey; | |
public string TableName; | |
} | |
} | |
// FROM XmlEncryption.cs | |
using System; | |
using System.IO; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Xml.Linq; | |
using Microsoft.AspNetCore.DataProtection.XmlEncryption; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Options; | |
namespace CustomDataProtection | |
{ | |
public class CustomXmlEncryptor : IXmlEncryptor | |
{ | |
private byte[] _key; | |
public CustomXmlEncryptor(IOptions<DataProtectionOptions> dataProtectionOptions) | |
{ | |
_key = dataProtectionOptions.Value.Key; | |
} | |
public EncryptedXmlInfo Encrypt(XElement plaintextElement) | |
{ | |
XElement encryptedElement = new XElement(plaintextElement); | |
CustomEncryption ge = new CustomEncryption(_key); | |
encryptedElement.Value = ge.Encrypt(plaintextElement.Value); | |
EncryptedXmlInfo encryptedTextElement = new EncryptedXmlInfo(encryptedElement, typeof(CustomXmlDecryptor)); | |
return encryptedTextElement; | |
} | |
} | |
public class CustomXmlDecryptor : IXmlDecryptor | |
{ | |
private byte[] _key; | |
public CustomXmlDecryptor(IServiceProvider services) | |
{ | |
var dataProtectionOptions = services.GetRequiredService<IOptions<DataProtectionOptions>>(); | |
_key = dataProtectionOptions.Value.Key; | |
} | |
public XElement Decrypt(XElement encryptedElement) | |
{ | |
XElement decryptedElement = new XElement(encryptedElement); | |
CustomEncryption ge = new CustomEncryption(_key); | |
decryptedElement.Value = ge.Decrypt(encryptedElement.Value); | |
return decryptedElement; | |
} | |
} | |
public class CustomEncryption | |
{ | |
private ICryptoTransform encryptor, decryptor; | |
private UTF8Encoding encoder; | |
private Aes alg; | |
private byte[] _key; | |
public CustomEncryption(byte[] key) | |
{ | |
_key = key; | |
alg = Aes.Create(); | |
encoder = new UTF8Encoding(); | |
} | |
public string Encrypt(string unencrypted) | |
{ | |
alg.GenerateIV(); | |
return GetString(Encrypt(encoder.GetBytes(unencrypted), alg.IV)) + ";" + GetString(alg.IV); | |
} | |
public string Decrypt(string encrypted) | |
{ | |
string[] splitStr = encrypted.Split(';'); | |
return encoder.GetString(Decrypt(GetBytes(splitStr[0]), GetBytes(splitStr[1]))); | |
} | |
private byte[] Encrypt(byte[] buffer, byte[] vector) | |
{ | |
encryptor = alg.CreateEncryptor(_key, vector); | |
return Transform(buffer, encryptor); | |
} | |
private byte[] Decrypt(byte[] buffer, byte[] vector) | |
{ | |
decryptor = alg.CreateDecryptor(_key, vector); | |
return Transform(buffer, decryptor); | |
} | |
private string GetString(byte[] input) | |
{ | |
return Convert.ToBase64String(input); | |
} | |
private byte[] GetBytes(string input) | |
{ | |
return Convert.FromBase64String(input); | |
} | |
protected byte[] Transform(byte[] buffer, ICryptoTransform transform) | |
{ | |
MemoryStream stream = new MemoryStream(); | |
using (CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Write)) | |
{ | |
cs.Write(buffer, 0, buffer.Length); | |
} | |
return stream.ToArray(); | |
} | |
} | |
} | |
// FROM XmlRepository.cs | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Xml.Linq; | |
using Microsoft.AspNetCore.DataProtection.Repositories; | |
using Microsoft.Extensions.Options; | |
namespace CustomDataProtection | |
{ | |
public class CustomXmlRepository : IXmlRepository | |
{ | |
string _storageAccount; | |
string _storageKey; | |
string _tableName; | |
public CustomXmlRepository(IOptions<DataProtectionOptions> dataProtectionOptions) | |
{ | |
_storageAccount = dataProtectionOptions.Value.StorageAccount; | |
_storageKey = dataProtectionOptions.Value.StorageKey; | |
_tableName = dataProtectionOptions.Value.TableName; | |
} | |
public IReadOnlyCollection<XElement> GetAllElements() | |
{ | |
IList<XElement> keyList = new List<XElement>(); | |
IEnumerable<DataProtectionKey> keyEntities = DataProtectionKey.FetchAll(_storageAccount, _storageKey, _tableName).Result; | |
foreach (var key in keyEntities) | |
{ | |
keyList.Add(XElement.Parse(key.Data)); | |
} | |
IReadOnlyCollection<XElement> readElements = new ReadOnlyCollection<XElement>(keyList); | |
return readElements; | |
} | |
public async void StoreElement(XElement element, string friendlyName) | |
{ | |
await DataProtectionKey.Insert(element.ToString(), _storageAccount, _storageKey, _tableName); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment