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 disadvantages. The advantages are the sharing of a library with other developers and the code changes are immediately reflected in the dependent 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 excellent 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:

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.

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

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 dependency 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.

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

Step 2

Install the PCLCrypto library with the Nuget tool.

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:

using System.Text;  
using PCLCrypto;  
namespace CryptoForms  
{
    /// <summary> 
    /// Common cryptographic helper 
    /// </summary> 
    public static class Crypto
    {
        /// <summary> 
        /// Creates Salt with given length in bytes. 
        /// </summary> 
        /// <returns></returns> 
        public static byte[] CreateSalt(uint lengthInBytes)
        {
            return WinRTCrypto.CryptographicBuffer.GenerateRandom(lengthInBytes);
        }
        /// <summary> 
        /// Creates a derived key from a comnination 
        /// </summary> 
        /// <returns></returns> 
        public static byte[] CreateDerivedKey(string password, byte[] salt, int keyLengthInBytes = 32,
            int iterations = 1000)
        {
            byte[] key = NetFxCrypto.DeriveBytes.GetBytes(password, salt, iterations, keyLengthInBytes);
            return key;
        }

        /// <summary> 
        /// Encrypts given data using symmetric algorithm AES 
        /// </summary> 
        ///Data to encrypt 
        ///Password 
        ///Salt 
        /// <returns>Encrypted bytes</returns> 
        public static byte[] EncryptAes(string data, string password, byte[] salt)
        {
            byte[] key = CreateDerivedKey(password, salt);
            ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
            ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);
            var bytes = WinRTCrypto.CryptographicEngine.Encrypt(symetricKey, Encoding.UTF8.GetBytes(data));
            return bytes;
        }

        /// <summary> 
        /// Decrypts given bytes using symmetric alogrithm AES 
        /// </summary> 
        ///data to decrypt 
        ///Password used for encryption 
        ///Salt used for encryption 
        /// <returns></returns> 
        public static string DecryptAes(byte[] data, string password, byte[] salt)
        {
            byte[] key = CreateDerivedKey(password, salt);
            ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
            ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);
            var bytes = WinRTCrypto.CryptographicEngine.Decrypt(symetricKey, data);
            return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
        }

    }
}

Step 4

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

using System;  
using Xamarin.Forms;  
namespace CryptoForms  
{
    public class MainPage : ContentPage
    {
        private Label label = null;
        private Button button = null;
        public MainPage()
        {
            button = new Button
            {
                Text = "Click me",
            };
            label = new Label
            {
                XAlign = TextAlignment.Center,
                Text = "Welcome to Xamarin Forms!"
            };
            button.Clicked += async (sender, e) =>
            {
                var data = "Cryptographic example"; var pass = "MySecretKey"; var contentPage = new ContentPage(); var salt = Crypto.CreateSalt(16); await contentPage.DisplayAlert("Alert", "Encrypting String " + data + ", with salt " + BitConverter.ToString(salt), "OK"); var bytes = Crypto.EncryptAes(data, pass, salt); await contentPage.DisplayAlert("Alert", "Encrypted, Now Decrypting", "OK"); var str = Crypto.DecryptAes(bytes, pass, salt); await contentPage.DisplayAlert("Alert", "Decryted " + str, "OK");
            };
            this.Title = "Crypto Forms"; // The root page of your application 
            this.Content = new StackLayout
            {
                VerticalOptions = LayoutOptions.Center,
                Children =
                {
                    label,
                    button
                }
            };
        }
    }
}

Step 5

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

Step 6

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

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.