Android Keystore based Encryption and Decryption in Xamarin Android

Brief Walkthrough on Android Keystore based app security in Xamarin android

Description
Data security should be considered as  top priority in any application and this is not just for password any sensitive data, app internal files including the database file. Any security breach in app secret data or app unique concepts(USP) can make way to competitors/attackers.  There are plenty of approaches one can choose to secure app private data.

Why Android Keystore based security : In general in any data encryption/decryption we need to provide the public/private keys. here the challenge is how we can secure these keys. Keystore based security provides solution for this issue where it generates and maintains the key dynamically. 


Let us get into the coding part:
Create Secret key:
  • Choose the alias name which is any non empty string to identify the key saved in keystore. 
  • Create an instance of AndroidKeyStore where it saves the key and it generates the key only if it is not created already.
  • Specify the algorithm used with keyGenerator and the purpose of the key used for i.e. encryption and decryption.
  • Specify the BlockModes that is going to use for encryption and decryption operation.
using Java.Security;
using Javax.Crypto;
using Android.Security.Keystore;
/// <summary>
/// Method returns the dynamic secret key generated from keystore. For the first time creates new key and for the subsequent calls returns already generated key
/// </summary>
/// <returns></returns>
private IKey GetKeyFromKeyStore()
{
var alias = "myAliasString";
var keystore = KeyStore.GetInstance("AndroidKeyStore");
keystore.Load(null);
if (!keystore.ContainsAlias(alias))
{
var keyGenerator = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, "AndroidKeyStore");
var keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeGcm)
.SetRandomizedEncryptionRequired(true)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingNone)
.Build();
keyGenerator.Init(keyGenParameterSpec);
return keyGenerator.GenerateKey();
}
return keystore.GetKey(alias, null);
}
Encrypt:
For encrption i have taken two examples:
1.File encryption it can be any file in device internal/external storage database file.
2.Plain text encryption, password or any string before saving to DB or sync.

1.File Encryption:
For file encrption just pass the existing file path and destination path where you want to save the encrypted file. (If you are encrypting and decrypting DB file then need to ensure that DB connections are closed)
  • Get the secret key by calling above mentioned method
  • Create the instance of Cipher by mentioning encryption type
  • Save the initialization vector(iv) from cipher which is going to use in decryption
  • Pass the bytes of source file to DoFinal method of cipher which returns bytes which is in encrypted format, save that bytes in destination file path
  • After encryption delete the original file
public static byte[] iv;
private void DoEncryption(string src, string dest)
{
IKey secretKey = GetKeyFromKeyStore();
Cipher cipher = Cipher.GetInstance("AES/GCM/NoPadding");
cipher.Init(Javax.Crypto.CipherMode.EncryptMode, secretKey);
iv = cipher.GetIV();
//var ivString = Base64.EncodeToString(iv, Base64Flags.NoWrap); //converted iv byte to string
var bytes = System.IO.File.ReadAllBytes(src);
var encryption = cipher.DoFinal(bytes);
System.IO.File.WriteAllBytes(dest, encryption);
if (System.IO.File.Exists(dest))
{
System.IO.File.Delete(src);
}
}
view raw DoEncryption.cs hosted with ❤ by GitHub

Decrypt:
  • In the same way as encryption get the secret key. 
  • Use the initialization vector which is already generated to create the GCMParameterSpec.
  • Here source file path points to the file created after above encryption process.
  • DoFinal method returns the bytes which is saved in destination file path and the decryped file.

private void DoDecryption(string src, string dest)
{
//iv = Base64.Decode(ivString , Base64Flags.NoWrap);
if (iv != null && iv.Length > 0)
{
var secretKey = GetKeyFromKeyStore();
Cipher cipher = Cipher.GetInstance("AES/GCM/NoPadding");
Javax.Crypto.Spec.GCMParameterSpec spec = new Javax.Crypto.Spec.GCMParameterSpec(128, iv);
cipher.Init(Javax.Crypto.CipherMode.DecryptMode, secretKey, spec);
var bytes = System.IO.File.ReadAllBytes(src);
byte[] decodedData = cipher.DoFinal(bytes);
System.IO.File.WriteAllBytes(dest, decodedData);
if (System.IO.File.Exists(dest))
{
System.IO.File.Delete(src);
}
}
}
view raw DoDecryption.cs hosted with ❤ by GitHub
2.String encryption and decryption:
Here the same steps will be followed as in file encryption/decryption except file path actual string is passed to encryption method and decryption method returns the string in original format nothing but decrypted string.
 
public static byte[] iv;
public byte[] DoEncryptionString(string strText)
{
var secretKey = GetKeyFromKeyStore();
Cipher cipher = Cipher.GetInstance("AES/GCM/NoPadding");
cipher.Init(Javax.Crypto.CipherMode.EncryptMode, secretKey);
iv = cipher.GetIV();
var encryption = cipher.DoFinal(System.Text.Encoding.UTF8.GetBytes(strText));
return encryption;
}
public string DoDecryptionString(byte[] strEncrypted)
{
var secretKey = GetKeyFromKeyStore();
Cipher cipher = Cipher.GetInstance("AES/GCM/NoPadding");
Javax.Crypto.Spec.GCMParameterSpec spec = new Javax.Crypto.Spec.GCMParameterSpec(128, iv);
cipher.Init(Javax.Crypto.CipherMode.DecryptMode, secretKey, spec);
byte[] decodedData = cipher.DoFinal(strEncrypted);
var data = System.Text.Encoding.UTF8.GetString(decodedData);
return data;
}

And we are done! Happy coding.

No comments:

Post a Comment