Encrypting and Decrypting Data in WinRT

As part of working on WinRT database I wanted to support ability to encrypt the data. The reason for that is that the data you persist using file system can be opened essentially in NotePad as long as you know where it is stored on the hard drive, which you can figure out. So, similarly to how I support encryption in Silverlight, I wanted to do the same in WinRT. I did some digging, and did find bits and pieces of information on the Win RT forum and in samples. Nothing however fully explained and had examples for encrypting and decrypting strings, which is what I wanted to accomplish. As a result, I wrote a helper class that I wanted to share.

My requirement were simple: just have two methods in a static class that take two parameters: input string and a password to use. I do not exactly use the password, but I use it to create a key: If you try to hack the code and pass password that was not used to encrypt into decryption method, you will get an exception. One thing that I found extremely annoying, is that all exceptions are coming in as COM exception (oh, no, do not take me back into the 90s – smile)

I added commented everywhere to make clear what I am doing. I coded this class using AES encryption provider. There is an ability in SymmetricKeyAlgorithmProvider.OpenAlgorithm to use various algorithms, but I could not find the list to save my life. As a result, I used the one mentioned in an example on the web – AES_CBC_PKCS7.

using System;
using System.Text;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
namespace EncryptDecrypt
{
/// <summary>
/// Class used for encryption and decryption using AES algorithm
/// </summary>
public static class EncryptionProvider
  {
    /// <summary>
    /// Encrypt a string
    /// </summary>
    /// <param name="input">String to encrypt</param>
    /// <param name="password">Password to use for encryption</param>
    /// <returns>Encrypted string</returns>
    public static string Encrypt(string input, string password)
    {
      //make sure we have data to work with
      if (string.IsNullOrEmpty(input))
        throw new ArgumentException("input cannot be empty");
      if (string.IsNullOrEmpty(password))
        throw new ArgumentException("password cannot be empty");
      // get IV, key and encrypt
      var iv = CreateInitializationVector(password);
      var key = CreateKey(password);
      var encryptedBuffer = CryptographicEngine.Encrypt(
        key, CryptographicBuffer.ConvertStringToBinary(input, BinaryStringEncoding.Utf8), iv);
      return CryptographicBuffer.EncodeToBase64String(encryptedBuffer);
    }
    /// <summary>
    /// Decrypt a string previously ecnrypted with Encrypt method and the same password
    /// </summary>
    /// <param name="input">String to decrypt</param>
    /// <param name="password">Password to use for decryption</param>
    /// <returns>Decrypted string</returns>
    public static string Decrypt(string input, string password)
    {
       //make sure we have data to work with
      if (string.IsNullOrEmpty(input))
         throw new ArgumentException("input cannot be empty");
       if (string.IsNullOrEmpty(password))
         throw new ArgumentException("password cannot be empty");
       // get IV, key and decrypt
       var iv = CreateInitializationVector(password);
       var key = CreateKey(password);
       var decryptedBuffer = CryptographicEngine.Decrypt(
         key, CryptographicBuffer.DecodeFromBase64String(input), iv);
       return CryptographicBuffer.ConvertBinaryToString(
        BinaryStringEncoding.Utf8, decryptedBuffer);
    }
    /// <summary>
    /// Create initialization vector IV
    /// </summary>
    /// <param name="password">Password is used for random vector generation</param>
    /// <returns>Vector</returns>
    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.SupportedKeyLengths.Min)
      {
        newPassword = newPassword + password;
      }
      //create vecotr
      var iv = CryptographicBuffer.CreateFromByteArray(
        UTF8Encoding.UTF8.GetBytes(newPassword));
      return iv;
    }
    /// <summary>
    /// Create encryption key
    /// </summary>
    /// <param name="password">Password is used for random key generation</param>
    /// <returns></returns>
    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.SupportedKeyLengths.Min)
      {
        newPassword = newPassword + password;
      }
      var buffer = CryptographicBuffer.ConvertStringToBinary(
        newPassword, BinaryStringEncoding.Utf8);
      buffer.Length = provider.SupportedKeyLengths.Min;
      var key = provider.CreateSymmetricKey(buffer);
      return key;
    }
  }
}

Using this class is very easy. Here is the example from my sample WinRT application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
namespace EncryptDecrypt
{
partial class MainPage
  {
    public MainPage()
    {
      InitializeComponent();
    }
    bool encrypt = true;
    private void Button_Click(object sender, RoutedEventArgs e)
    {
       var password = this.password.Text;
       if (encrypt)
       {
          this.input.Text = this.input.Text;
          this.ouput.Text = EncryptDecrypt.EncryptionProvider.Encrypt(this.input.Text, password);
          this.button.Content = "Decrypt";
          encrypt = false;
      }
      else
      {
          this.input.Text = this.ouput.Text;
          this.ouput.Text = EncryptDecrypt.EncryptionProvider.Decrypt(this.ouput.Text, password);
          this.button.Content = "Encrypt";
         encrypt = true;
      }
    }
  }
}

As you can see, my sample test app just calls two main methods and shows the results of encryption and decryption. Password and input string are required and cannot be left empty.

I am hoping to expand this in the future, once documentation is more complete, but this suffices for now. Please feel free to comment on this, especially if you have more information on the subject.

Thanks.