First we should create a Storage Helper:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows.Storage;
using System.IO;
using Windows.Storage.Streams;
using System.Xml.Linq;
using IsoStorageSample.Helpers;
namespace IsoStorageSample.Helpers
{
public enum StorageType { Local, Temporary, Roaming }
public class IsoStorage<T>
{
protected ApplicationData appData = Windows.Storage.ApplicationData.Current;
protected StorageFolder storageFolder;
protected StorageType storageType;
public StorageType StorageType
{
get { return storageType; }
set
{
storageType = value;
// set the storage folder
switch (storageType)
{
case StorageType.Local:
storageFolder = appData.LocalFolder;
break;
case StorageType.Temporary:
storageFolder = appData.TemporaryFolder;
break;
case StorageType.Roaming:
storageFolder = appData.RoamingFolder;
break;
default:
throw new Exception(String.Format("Unknown StorageType: {0}", storageType));
}
}
}
public IsoStorage() : this(StorageType.Local) { }
public IsoStorage(StorageType type)
{
StorageType = type;
}
public async Task CopyAsync(StorageFolder folder, string fileName)
{
try
{
StorageFile storageFile = await folder.GetFileAsync(fileName);
if (await GetFileIfExistsAsync(storageFile.Name) == null)
{
await storageFile.CopyAsync(storageFolder);
}
}
catch (Exception ex)
{
throw ex;
}
}
public async Task<XDocument> LoadXml(string fileName)
{
try
{
StorageFile xmlFile = await GetFileIfExistsAsync(fileName);
if (xmlFile != null)
{
return XDocument.Load(xmlFile.Path);
}
else
{
return null;
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Delete a file asynchronously
/// </summary>
/// <param name="fileName"></param>
public async void DeleteAsync(string fileName)
{
try
{
fileName = AppendExt(fileName);
var file = await GetFileIfExistsAsync(fileName);
if (file != null)
await file.DeleteAsync();
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// At the moment the only way to check if a file exists to catch an exception... :/
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
protected async Task<StorageFile> GetFileIfExistsAsync(string fileName)
{
try
{
return await storageFolder.GetFileAsync(fileName);
}
catch
{
return null;
}
}
/// <summary>
/// Appends the file extension to the given filename
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
protected string AppendExt(string fileName)
{
if (fileName.Contains(".txt"))
return fileName;
else
return string.Format("{0}.txt", fileName);
}
/// <summary>
/// Saves a serialized object to storage asynchronously
/// </summary>
/// <param name="filename"></param>
/// <param name="obj"></param>
public async void SaveAsync(string fileName, T data, string key = null)
{
try
{
if (data == null)
return;
fileName = AppendExt(fileName);
var file = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
string serializedObject = JSONSerializerHelper.Serialize(data);
string encryptedObject = serializedObject;
if (key != null)
{
encryptedObject = Hashing.Encrypt(serializedObject, key);
}
await FileIO.WriteTextAsync(file, encryptedObject);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Load a given filename asynchronously
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public async Task<T> LoadAsync(string fileName, string key = null)
{
try
{
fileName = AppendExt(fileName);
StorageFile file = null;
file = await storageFolder.GetFileAsync(fileName);
string serializedObject = await FileIO.ReadTextAsync(file);
string decryptedObject = serializedObject;
if (key != null)
{
decryptedObject = Hashing.Decrypt(serializedObject, key);
}
return (T)JSONSerializerHelper.DeSerialize(typeof(T), decryptedObject);
}
catch (FileNotFoundException)
{
//file not existing is perfectly valid so simply return the default
return default(T);
//throw;
}
catch (Exception)
{
//Unable to load contents of file
throw;
}
}
}
}
For serializing data, I use Json.NET and create a helper for that:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IsoStorageSample.Helpers
{
public static class JSONSerializerHelper
{
public static string Serialize(object obj)
{
string json = JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, TypeNameHandling = TypeNameHandling.Objects });
return json;
}
public static object DeSerialize(Type type, string str)
{
object json = JsonConvert.DeserializeObject(str, type, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, TypeNameHandling = TypeNameHandling.Objects });
return json;
}
}
}
And for hashing data I use Windows.Security.Cryptography
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
namespace IsoStorageSample.Helpers
{
public class Hashing
{
public static string Encrypt(string dataToEncrypt, string myKey)
{
//make sure we have data to work with
if (string.IsNullOrEmpty(dataToEncrypt))
throw new ArgumentException("input cannot be empty");
if (string.IsNullOrEmpty(myKey))
throw new ArgumentException("password cannot be empty");
// get IV, key and encrypt
var iv = CreateInitializationVector(myKey);
var key = CreateKey(myKey);
var encryptedBuffer = CryptographicEngine.Encrypt(key, CryptographicBuffer.ConvertStringToBinary(dataToEncrypt, BinaryStringEncoding.Utf8), iv);
return CryptographicBuffer.EncodeToBase64String(encryptedBuffer);
}
public static string Decrypt(string encryptedString, string myKey)
{
try
{
//make sure we have data to work with
if (string.IsNullOrEmpty(encryptedString))
throw new ArgumentException("input cannot be empty");
if (string.IsNullOrEmpty(myKey))
throw new ArgumentException("password cannot be empty");
// get IV, key and decrypt
var iv = CreateInitializationVector(myKey);
var key = CreateKey(myKey);
var decryptedBuffer = CryptographicEngine.Decrypt(key, CryptographicBuffer.DecodeFromBase64String(encryptedString), iv);
return CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, decryptedBuffer);
}
catch (Exception)
{
throw;
}
}
private static IBuffer CreateInitializationVector(string password)
{
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7");
var newPassword = password;
// make sure we satify minimum length requirements
while (newPassword.Length < provider.BlockLength)
{
newPassword = newPassword + password;
}
//create vecotr
var iv = CryptographicBuffer.CreateFromByteArray(UTF8Encoding.UTF8.GetBytes(newPassword));
return iv;
}
private static CryptographicKey CreateKey(string password)
{
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7");
var newPassword = password;
// make sure we satify minimum length requirements
while (newPassword.Length < provider.BlockLength)
{
newPassword = newPassword + password;
}
var buffer = CryptographicBuffer.ConvertStringToBinary(newPassword, BinaryStringEncoding.Utf8);
buffer.Length = provider.BlockLength;
var key = provider.CreateSymmetricKey(buffer);
return key;
}
}
}
Now we can use that like this:
private void SaveData()
{
Dictionary<int, Person> persons = new Dictionary<int, Person>();
persons.Add(1, new Person { ID = 1, Name = "Michael" });
persons.Add(2, new Person { ID = 2, Name = "John" });
persons.Add(3, new Person { ID = 3, Name = "Rose" });
IsoStorage<Dictionary<int, Person>> storage = new IsoStorage<Dictionary<int, Person>>();
//if key is not equal null => data will be encrypt
storage.SaveAsync("TestStorage", persons, null);
}
private async void LoadData()
{
IsoStorage<Dictionary<int, Person>> storage = new IsoStorage<Dictionary<int, Person>>();
Dictionary<int, Person> persons = await storage.LoadAsync("TestStorage", null);
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
}
Download full sample from here
No comments:
Post a Comment