Using Cryptography in Portable Xamarin Forms (Windows Phone, Android and IOS)

Xamarin forms allow us to develop cross-platform (Windows Phone, Android and iOS) apps with a common UI project and with native look & feel. This common UI project can be a shared project or portable library.

Shared projects are used when common code must be shared among all the platforms and if needed allows the use of custom code targeting specific platforms with debug symbols whereas portable projects allow us to create common code irrespective of the platform and is limited to common features available in all the platforms. It doesn't allow developers to write code for a specific platform. This approach has both advantages and disadvantagoes. The advantages are the sharing of a library with other developers and the code changes are immediately reflected in the depedent projects. The disadvantages are that no-platform-specific code is allowed and is limited to common features of all the platforms it supports and cannot reference platform-specific libraries.

Recently I have been working on a project to develop native apps in all the three platforms (Windows Phone, Android and iPhone) with C# and Xamarin forms. One of the requirements is to use cryptography features to encrypt and decrypt user data based on the password supplied.

.Net provides exellent cryptographic features under the namespace System.Security.Cryptography. The intended use of this library is for .Net development in Windows. It cannot be used in Windows Phone and Silverlight and Xamarin portable forms cannot use them.

Some of the alternatives are:

  1. PCL Contrib: Community developed project. Supports cryptographic features such as AES, Derived key algorithms. The advantage is it uses the same namespaces as .Net cryptography. Not frequently updated: Portable Class Libraries Contrib.

  2. Bouncy Castle PCL: Portable Class Library version of Bouncy Castle Cryptography with many criptographic alogrithms: A modification of C# Bouncy Castle to be usable with PCL's.

  3. PCL Crypto: Portable Class Library. No proper documentation. Supports all the Xamarin platforms: Cryptography for portable class libraries.

    The other work-around is to use some dependency injection with platform-specific code in each platform library and using it in common projects. Xamarin forms provide a minimum depedency feature using DependencyService. The only issue here is writing platform-specific code and it is time consuming: Accessing Native Features via the DependencyService.

I have decided to use the PCL Crypto library since it is easier and flexible to use. I'll explain how to configure and use PCL crypto with a sample project. The task is to encrypt and decrypt user passwords with the AES CBC algorithm using an AES 256bit cryptographic key. The key is generated from a combination of password and randomly generated salt using a PBKDF2 derived key algorithmic function.

Step 1

Create a Xamarin forms portable application.

blank app

Visual Studio creates the following 4 projects, one common portable and three platform-specific libraries.

cryptoforms

Step 2

Install the PCLCrypto library with the Nuget tool.

Install pcl

Important: the PCL Crypto libraries must be added to each platform and common library. Common cryptographic code can be added in the common portable. Uncheck iOS library if you get an error during install.

Step 3

Create a cryptographic helper class in the Common Portable Library (CryptoForms).

Add the following code in Crypto.cs:

  1. using System.Text;    
  2. using PCLCrypto;    
  3.     
  4. namespace CryptoForms    
  5. {    
  6.     /// <summary>    
  7.     /// Common cryptographic helper    
  8.     /// </summary>    
  9.     public static class Crypto    
  10.     {    
  11.         /// <summary>    
  12.         /// Creates Salt with given length in bytes.    
  13.         /// </summary>    
  14.         /// <param name="lengthInBytes">No. of bytes</param>    
  15.         /// <returns></returns>    
  16.         public static byte[] CreateSalt(uint lengthInBytes)    
  17.         {    
  18.             return WinRTCrypto.CryptographicBuffer.GenerateRandom(lengthInBytes);    
  19.         }    
  20.     
  21.         /// <summary>    
  22.         /// Creates a derived key from a comnination     
  23.         /// </summary>    
  24.         /// <param name="password"></param>    
  25.         /// <param name="salt"></param>    
  26.         /// <param name="keyLengthInBytes"></param>    
  27.         /// <param name="iterations"></param>    
  28.         /// <returns></returns>    
  29.         public static byte[] CreateDerivedKey(string password, byte[] salt, int keyLengthInBytes = 32, int iterations = 1000)    
  30.         {    
  31.             byte[] key = NetFxCrypto.DeriveBytes.GetBytes(password, salt, iterations, keyLengthInBytes);    
  32.             return key;    
  33.         }    
  34.          
  35.         /// <summary>    
  36.         /// Encrypts given data using symmetric algorithm AES    
  37.         /// </summary>    
  38.         /// <param name="data">Data to encrypt</param>    
  39.         /// <param name="password">Password</param>    
  40.         /// <param name="salt">Salt</param>    
  41.         /// <returns>Encrypted bytes</returns>    
  42.         public static byte[] EncryptAes(string data, string password, byte[] salt)    
  43.         {    
  44.             byte[] key = CreateDerivedKey(password, salt);    
  45.            
  46.             ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);    
  47.             ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);    
  48.             var bytes = WinRTCrypto.CryptographicEngine.Encrypt(symetricKey, Encoding.UTF8.GetBytes(data));    
  49.             return bytes;    
  50.         }    
  51.         /// <summary>    
  52.         /// Decrypts given bytes using symmetric alogrithm AES    
  53.         /// </summary>    
  54.         /// <param name="data">data to decrypt</param>    
  55.         /// <param name="password">Password used for encryption</param>    
  56.         /// <param name="salt">Salt used for encryption</param>    
  57.         /// <returns></returns>    
  58.         public static string DecryptAes(byte[] data, string password, byte[] salt)    
  59.         {    
  60.             byte[] key = CreateDerivedKey(password, salt);    
  61.               
  62.             ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);    
  63.             ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);    
  64.             var bytes = WinRTCrypto.CryptographicEngine.Decrypt(symetricKey, data);    
  65.             return Encoding.UTF8.GetString(bytes, 0, bytes.Length);    
  66.         }    
  67.     
  68.     }    
  69. }   

Step 4

Create a new form in the common portable and set it as the main page in App.cs:

main page

  1. using System;    
  2. using Xamarin.Forms;    
  3.     
  4. namespace CryptoForms    
  5. {    
  6.     public class MainPage : ContentPage    
  7.     {    
  8.     
  9.         private Label label = null;    
  10.         private Button button = null;    
  11.     
  12.         public MainPage()    
  13.         {    
  14.             button = new Button    
  15.             {    
  16.                 Text = "Click me",    
  17.             };    
  18.     
  19.             label = new Label    
  20.             {    
  21.                 XAlign = TextAlignment.Center,    
  22.                 Text = "Welcome to Xamarin Forms!"    
  23.             };    
  24.             button.Clicked += async (sender, e) =>    
  25.             {    
  26.                 var data = "Cryptographic example";    
  27.                 var pass = "MySecretKey";    
  28.     
  29.                 var contentPage = new ContentPage();    
  30.                 var salt = Crypto.CreateSalt(16);    
  31.                 await contentPage.DisplayAlert("Alert""Encrypting String " + data + ", with salt " + BitConverter.ToString(salt), "OK");    
  32.                 var bytes = Crypto.EncryptAes(data, pass, salt);    
  33.                 await contentPage.DisplayAlert("Alert""Encrypted, Now Decrypting""OK");    
  34.                 var str = Crypto.DecryptAes(bytes, pass, salt);    
  35.                 await contentPage.DisplayAlert("Alert""Decryted " + str, "OK");    
  36.             };    
  37.     
  38.             this.Title = "Crypto Forms";    
  39.             // The root page of your application    
  40.             this.Content = new StackLayout    
  41.             {    
  42.                 VerticalOptions = LayoutOptions.Center,    
  43.                 Children =    
  44.                 {    
  45.                     label,    
  46.                     button    
  47.     
  48.                 }    
  49.             };    
  50.     
  51.     
  52.         }    
  53.     }    
  54. }  

Step 5

Set CryptoForms.Driod as the Startup project and run. See the output in the Android Emulator below:

Set CryptoForms

Step 6

Set CryptoForms.WinPhone as the Startup project and run. See the output in the Windows Phone emulator.

WinPhone as Startup

I have not modified any code in each platform-specific library. The advantage of a common portable is to have common code for all the platforms and Xamarin, on build, converts to platform-specifc code.

I have some problems in setting up the iOS environment since I don't have a Mac book. The alternative is to run the Mac OS in a virtual box to build and deploy the iOS app. iOS 64 bit must be installed but the virtual box doesn't support 64-bit guest OS when hyper-v is enabled in Windows features. Windows Phone runs only when hyper-v is enabled so testing is cumbersome with a couple of restarts for each Windows Phone and iOS.