Cryptography in Portable Libraries

When we think of using Cryptography in Xamarin Forms with a portable library we normally end up looking for a Nuget Package. In most cases we come across PCLCrypto, I have used it in the past and it has been great but I never liked adding additional packages to my projects and now we no longer need to.

cryptography login

Using Cryptography in .NET Standard

If you have converted your Xamarin Forms project to .NET Standard and you have chosen 1.3 or higher as your target, then you can enjoy doing most common cryptographic functions in the standard libraries. While I do not know of the API coverage differences between PCLCrypto and System.Security.Cryptography, I have found it served both of my needs, which was hashing and encryption/decryption.

To use this namespace in your Portable library just set your .NET Standard target to 1.3 or higher and it automatically becomes available. Below is example code in C# on how to implement hashing and encryption.

Hashing

Hashing is the process of doing non-reversible encryption. For example if you want to If you need to do some simple hashing when storing a password (if you aren’t preferably doing OAuth), then here is a simple function to get started. It converts it back to a Base64 string which is one of the ways to ensure that all bytes are properly recorded, as UTF8 doesn’t have an exact byte match for each possible byte sequence.

using System.Security.Cryptography;
public static string HashSHA512(this string value)
{
    using (var sha = SHA512.Create())
    {
       return Convert.ToBase64String(sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(value)));
    }
}

If you want to add a salt to the hash, all you need to do is append a random string to the end of the value. You can store the random string next to each hashed value. Salts do not need to be kept secret and should be different for each hash. The reason we do this is to reduce the impact of pre-built hash tables. For example if I hash the password ‘password’, it will come up as the same Hash on each computer. As such I can easily compare a prebuilt hash to see if anyone is using this password. It also stops the hashes of two people with the same passwords from being identical.

AES Encryption

If you need reversible encryption then AES is generally the recommended standard. I use AES 256, which requires a 32 byte key to encrypt. But we can either go and generate a 32 byte key somewhere or we can use any string we want and convert it into a key with the below.

private static byte[] CreateKey(string password, int keyBytes = 32)
{
    byte[] salt = new byte[] { 80, 70, 60, 50, 40, 30, 20, 10 };
    int iterations = 300;
    var keyGenerator = new Rfc2898DeriveBytes(password, salt, iterations);
    return keyGenerator.GetBytes(keyBytes);
}

Note that the salt will remain the same here for each key generated, we do not need to randomize the salt as we did in with hashing.

Encryption

On to the encryption, using the Encrypt function you pass in the value to encrypt and the key. It will return the encrypted text with the IV appended to the end. In AES, the IV is a randomizer, making sure that no two identical messages ever give the same encrypted output. The IV can be public and does not need to be hidden.

public static string Encrypt(this string clearValue, string encryptionKey)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = CreateKey(encryptionKey);
 
        byte[] encrypted = AesEncryptStringToBytes(clearValue, aes.Key, aes.IV);
        return Convert.ToBase64String(encrypted) + ";" + Convert.ToBase64String(aes.IV);
    }
}
private static byte[] AesEncryptStringToBytes(string plainText, byte[] key, byte[] iv)
{
     if (plainText == null || plainText.Length <= 0)
         throw new ArgumentNullException($"{nameof(plainText)}");
     if (key == null || key.Length <= 0)
         throw new ArgumentNullException($"{nameof(key)}");
     if (iv == null || iv.Length <= 0)
         throw new ArgumentNullException($"{nameof(iv)}");

     byte[] encrypted;
 
     using (Aes aes = Aes.Create())
     {
         aes.Key = key;
         aes.IV = iv;

         using (MemoryStream memoryStream = new MemoryStream())
         {
             using (ICryptoTransform encryptor = aes.CreateEncryptor())
             using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
             using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
             {
                  streamWriter.Write(plainText);
             }
             encrypted = memoryStream.ToArray();
         }
     }
     return encrypted;
}

Decryption

Decryption requires the encrypted value and the secret key. This function is directly related to the above encryption process which will automatically get the IV from the end of the string passed in. Please keep this in mind if you are trying to decrypt a message encrypted by another function. You will most likely have to pass in the IV yourself.

public static string Decrypt(this string encryptedValue, string encryptionKey)
{
    string iv = encryptedValue.Substring(encryptedValue.IndexOf(';') + 1, encryptedValue.Length - encryptedValue.IndexOf(';') - 1);
    encryptedValue = encryptedValue.Substring(0, encryptedValue.IndexOf(';'));

    return AesDecryptStringFromBytes(Convert.FromBase64String(encryptedValue), CreateKey(encryptionKey), Convert.FromBase64String(iv));
}
private static string AesDecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv)
{
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException($"{nameof(cipherText)}");
    if (key == null || key.Length <= 0)
        throw new ArgumentNullException($"{nameof(key)}");
    if (iv == null || iv.Length <= 0)
        throw new ArgumentNullException($"{nameof(iv)}");

    string plaintext = null;

    using (Aes aes = Aes.Create())
    {
         aes.Key = key;
         aes.IV = iv;

         using (MemoryStream memoryStream = new MemoryStream(cipherText))
         using (ICryptoTransform decryptor = aes.CreateDecryptor())
         using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
         using (StreamReader streamReader = new StreamReader(cryptoStream))
             plaintext = streamReader.ReadToEnd();

    }
    return plaintext;
}

Summary

These functions are quick and easy to implement and I use them with success in my new projects. I have not tested performance and I have not needed to. Generally hashing performance is dictated in number of hashes per second or similar. My app only needs to hash a single value every few months at most, hence I don’t need to worry if a native implementation can do a million more hashes per second. Depending on your app this may be a consideration you need to account for but it is unlikely.

Microsoft MVP | Xamarin MVP | Xamarin Certified Developer |
Exrin MVVM Framework | Xamarin Forms Developer | Melbourne, Australia

Related Posts

8 Comments

  1. Matt

    Hi there,

    I am trying to attempt to use cryptography in my Xamarin project and I get an error when running tests in Test Explorer (Not when running on android though).

    System.IO.FileLoadException : Could not load file or assembly ‘System.Security.Cryptography.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

  2. Greg

    Hi Adam,
    Thanks for sharing! I need to do SQLite encryption. I am new to this encryption subject, so what would be needed to do this? SQL Cipher does this at the file level, but if I understand right your solution would be used only to Encrypt the data that we put in the DB. But in that case how would you query the encrypted data?
    Or am I missing something?
    Thanks a lot,
    Greg

    1. Adam Pedley

      It all depends upon your needs. If everything, or a lot of data needs to be encrypted, then SQLCipher may be the best option.

      However, I normally find that only a small section of data needs to be encrypted, or maybe only a column or 2. As such you can still search on other columns, get the data back and decrypt it as needed.

  3. Raphaël

    Hello,
    It’s a really good article thank you!
    However, I don’t understand why the salt does not need to be randomized? Could you elaborate on this point?
    I’ve read somewhere else that the salt needs to be changed for each encryption, however when I do this with a RNGCryptoServiceProvider I have trouble to decrypt using your code.

    Thank you

    1. Adam Pedley

      AES encryption uses the Key and IV. The IV will change each time, you can almost think of the IV as a salt.

      The salt in this encryption example is just a to create a key, and it’s the key and IV that are used in the encryption. The IV changes each time, to avoid the same key generating the same encrypted message. But you would always want the same password to generate the same key, so that it can be decrypted.

      It’s not the same as a salt in a hash.

Leave A Comment?