Skip to content

Instantly share code, notes, and snippets.

@guardrex
Last active October 19, 2016 23:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guardrex/7342544cab59cd9cbf3a22f0104b8fdd to your computer and use it in GitHub Desktop.
Save guardrex/7342544cab59cd9cbf3a22f0104b8fdd to your computer and use it in GitHub Desktop.
// 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