Simple Encryption for Windows, WinRT and Windows Phone

Having three different platforms involved in one project can be challenging. Especially when it comes to missing API’s or similar API’s but with different behaviour. In my case it was encryption. I already had things running with a WPF admin client and Windows Phone app for customers. The WPF client encrypts and uploads a data to Azure Storage from where it is consumed by the Windows Phone app. As encryption I use the symmetric-key algorithm AES. The full .NET framework and the Windows Phone platform provides the AesManaged class that implements the AES algorithm. That worked just fine till I started the development on the Windows 8 version of the very same app and I discovered that WinRT doesn’t provide the AesManaged class. But sure enough, a AES implementation in the Windows.Security.Cryptography namespace could be found. However, I couldn’t managed to get the file decrypted on the WinRT side. Till I found a post by Ginny Caughey with good sample code that worked for all three platforms. After fighting some encoding issues, I came up with the code of an little encryption helper class that is mainly based on Ginny’s implementation. Thanks again Ginny! The class can be used to encrypt/decrypt data on all three platforms. I hope this helps other that face the very same problem.

public static class EncryptionHelper
    {

#if NETFX_CORE
        public static byte[] Encrypt(string plainText, string pw, string salt)
        {
            IBuffer pwBuffer = CryptographicBuffer.ConvertStringToBinary(pw, BinaryStringEncoding.Utf8);
            IBuffer saltBuffer = CryptographicBuffer.ConvertStringToBinary(salt, BinaryStringEncoding.Utf16LE);
            IBuffer plainBuffer = CryptographicBuffer.ConvertStringToBinary(plainText, BinaryStringEncoding.Utf16LE);

            // Derive key material for password size 32 bytes for AES256 algorithm
            KeyDerivationAlgorithmProvider keyDerivationProvider = Windows.Security.Cryptography.Core.KeyDerivationAlgorithmProvider.OpenAlgorithm("PBKDF2_SHA1");
            // using salt and 1000 iterations
            KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2(saltBuffer, 1000);

            // create a key based on original key and derivation parmaters
            CryptographicKey keyOriginal = keyDerivationProvider.CreateKey(pwBuffer);
            IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial(keyOriginal, pbkdf2Parms, 32);
            CryptographicKey derivedPwKey = keyDerivationProvider.CreateKey(pwBuffer);

            // derive buffer to be used for encryption salt from derived password key
            IBuffer saltMaterial = CryptographicEngine.DeriveKeyMaterial(derivedPwKey, pbkdf2Parms, 16);

            // display the buffers – because KeyDerivationProvider always gets cleared after each use, they are very similar unforunately
            string keyMaterialString = CryptographicBuffer.EncodeToBase64String(keyMaterial);
            string saltMaterialString = CryptographicBuffer.EncodeToBase64String(saltMaterial);

            SymmetricKeyAlgorithmProvider symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7");
            // create symmetric key from derived password key
            CryptographicKey symmKey = symProvider.CreateSymmetricKey(keyMaterial);

            // encrypt data buffer using symmetric key and derived salt material
            IBuffer resultBuffer = CryptographicEngine.Encrypt(symmKey, plainBuffer, saltMaterial);
            byte[] result;
            CryptographicBuffer.CopyToByteArray(resultBuffer, out result);

            return result;
        }

        public static string Decrypt(byte[] encryptedData, string pw, string salt)
        {
            IBuffer pwBuffer = CryptographicBuffer.ConvertStringToBinary(pw, BinaryStringEncoding.Utf8);
            IBuffer saltBuffer = CryptographicBuffer.ConvertStringToBinary(salt, BinaryStringEncoding.Utf16LE);
            IBuffer cipherBuffer = CryptographicBuffer.CreateFromByteArray(encryptedData);

            // Derive key material for password size 32 bytes for AES256 algorithm
            KeyDerivationAlgorithmProvider keyDerivationProvider = Windows.Security.Cryptography.Core.KeyDerivationAlgorithmProvider.OpenAlgorithm("PBKDF2_SHA1");
            // using salt and 1000 iterations
            KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2(saltBuffer, 1000);

            // create a key based on original key and derivation parmaters
            CryptographicKey keyOriginal = keyDerivationProvider.CreateKey(pwBuffer);
            IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial(keyOriginal, pbkdf2Parms, 32);
            CryptographicKey derivedPwKey = keyDerivationProvider.CreateKey(pwBuffer);

            // derive buffer to be used for encryption salt from derived password key
            IBuffer saltMaterial = CryptographicEngine.DeriveKeyMaterial(derivedPwKey, pbkdf2Parms, 16);

            // display the keys – because KeyDerivationProvider always gets cleared after each use, they are very similar unforunately
            string keyMaterialString = CryptographicBuffer.EncodeToBase64String(keyMaterial);
            string saltMaterialString = CryptographicBuffer.EncodeToBase64String(saltMaterial);

            SymmetricKeyAlgorithmProvider symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7");
            // create symmetric key from derived password material
            CryptographicKey symmKey = symProvider.CreateSymmetricKey(keyMaterial);

            // encrypt data buffer using symmetric key and derived salt material
            IBuffer resultBuffer = CryptographicEngine.Decrypt(symmKey, cipherBuffer, saltMaterial);
            string result = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16LE, resultBuffer);
            return result;
        }

#else

        public static byte[] Encrypt(string dataToEncrypt, string password, string salt)
        {
            AesManaged aes = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                //Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

                //Create AES algorithm with 256 bit key and 128-bit block size
                aes = new AesManaged();
                aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
                rfc2898.Reset(); //needed for WinRT compatibility
                aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);

                //Create Memory and Crypto Streams
                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);

                //Encrypt Data
                byte[] data = Encoding.Unicode.GetBytes(dataToEncrypt);
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();

                //Return encrypted data
                return memoryStream.ToArray();
              
            }
            catch (Exception eEncrypt)
            {
                return null;
            }
            finally
            {
                if (cryptoStream != null)
                    cryptoStream.Close();

                if (memoryStream != null)
                    memoryStream.Close();

                if (aes != null)
                    aes.Clear();

            }
        }

        public static string Decrypt(byte[] dataToDecrypt, string password, string salt)
        {
            AesManaged aes = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;
            string decryptedText = "";
            try
            {
                //Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

                //Create AES algorithm with 256 bit key and 128-bit block size
                aes = new AesManaged();
                aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
                rfc2898.Reset(); //neede to be WinRT compatible
                aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);

                //Create Memory and Crypto Streams
                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write);

                //Decrypt Data
                cryptoStream.Write(dataToDecrypt, 0, dataToDecrypt.Length);
                cryptoStream.FlushFinalBlock();

                //Return Decrypted String
                byte[] decryptBytes = memoryStream.ToArray();
                decryptedText = Encoding.Unicode.GetString(decryptBytes, 0, decryptBytes.Length);
            }
            catch (Exception eDecrypt)
            {
   
            }
            finally
            {
                if (cryptoStream != null)
                    cryptoStream.Close();

                if (memoryStream != null)
                    memoryStream.Close();

                if (aes != null)
                    aes.Clear();
            }
            return decryptedText;
        }
#endif

    }

8 thoughts on “Simple Encryption for Windows, WinRT and Windows Phone

    1. Hi Jan

      this is what i am using in my silverlight windows phone project.
      its working well
      but its not working in Windows RT project there is no namespace in Windows RT project
      public static string Decrypt(string base64StringToDecrypt)
      {
      if (string.IsNullOrEmpty(base64StringToDecrypt))
      return string.Empty;
      //Set up the encryption objects
      using (Aes acsp = GetProvider(Encoding.UTF8.GetBytes(Constants.EncryptionKey)))
      {
      byte[] RawBytes = Convert.FromBase64String(base64StringToDecrypt);
      ICryptoTransform ictD = acsp.CreateDecryptor();
      //RawBytes now contains original byte array, still in Encrypted state
      //Decrypt into stream
      MemoryStream msD = new MemoryStream(RawBytes, 0, RawBytes.Length);
      CryptoStream csD = new CryptoStream(msD, ictD, CryptoStreamMode.Read);
      //csD now contains original byte array, fully decrypted
      //return the content of msD as a regular string
      return (new StreamReader(csD)).ReadToEnd();
      }
      }

      private static Aes GetProvider(byte[] key)
      {
      Aes result = new AesManaged();
      result.GenerateIV();
      result.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
      byte[] RealKey = GetKey(key, result);
      result.Key = RealKey;
      return result;
      }

      private static byte[] GetKey(byte[] suggestedKey, SymmetricAlgorithm p)
      {
      byte[] kRaw = suggestedKey;
      List kList = new List();
      for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8)
      {
      kList.Add(kRaw[(i / 8) % kRaw.Length]);
      }
      byte[] k = kList.ToArray();
      return k;
      }

      can u please provide me solution so i can use it in Windows RT project
      Thanks.

  1. Great post!
    One question though: If I would like to use the encrypted value to send it across to a webservice, it would have to be converted to string. I searched on that, and tried it out, but cannot seem to get it working, maybe I’m messing up something about encoding.
    Could you help me on that? How to convert this to string so I can send and receive it across http(s)

    thanks
    Martin

  2. Wonderful !. So far it is what comes closest. To decrypt a text NETFX_CORE method, and that was encrypted with the desktop, the first 16 bytes come damaged.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s