Encryption Tutorial For Android: Getting Started

Ever wondered how you can use data encryption to secure your private user data from hackers? Look no more, in this tutorial you’ll do just that! By Kolin Stürt.

Leave a rating/review
Download materials
Save for later
Share

With all the recent data breaches and new privacy laws, such as GDPR, your app’s credibility depends on how you manage your user’s data. There are powerful Android APIs focusing on data encryption that are sometimes overlooked when beginning a project. You can put them to great use and think of security from the ground up.

In this tutorial, you’ll secure an app for veterinary clinics that store medical information. During the process, you’ll learn how to:

  • Tighten app permissions
  • Encrypt your data
  • Use the KeyStore

Note: This tutorial assumes that you’re already familiar with the basics of Android development and Android Studio. If Android development is new to you, first read through the Beginning Android Development and Kotlin for Android tutorials.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of this tutorial. Take a moment to familiarize yourself with the structure of the project. Build and run the app to see what you’re working with.

You’ll see a simple sign-up screen. Once you enter a password and choose Signup, you’ll be prompted for that password on subsequent app launches. After that step, you’ll get a list of pets. Most of the app is complete, so you’ll focus on securing it. Tap an entry in the list to reveal the pet’s medical information:

Pet details screen

If on Android 7+, you get a crash with error java.lang.SecurityException: MODE_WORLD_READABLE no longer supported, don’t worry. You’ll fix it soon.

Securing the foundations

To begin encrypting your applications, and securing important data, you first have to prevent leaking data to the rest of the world. When it comes to Android, this usually means protecting your user-based data from being read by any other application, and limiting the location where the applications are installed. Let’s do this first, so you can start encrypting private information.

Using Permissions

When you first start out to build your app, it’s important to think about how much user-data you actually need to keep. These days, the best practice is to avoid storing private data if you don’t have to — especially for our cute little Lightning, who is concerned about his privacy.

Ever since Android 6.0, files and SharedPreferences you save are set with the MODE_PRIVATE constant. That means only your app can access the data. Android 7 doesn’t allow any other option. So first things first, you’ll make sure the project is set up securely.

Open the MainActivity.kt file. You’ll notice there are two deprecation warnings for MODE_WORLD_READABLE and MODE_WORLD_WRITABLE. These allow public access to your files on earlier Android versions. Find the line that sets MODE_WORLD_WRITABLE and replace it with the following:

val preferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)

Then, find the line that sets MODE_WORD_READABLE and replace it with this:

val editor = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE).edit()

Great, you’ve just made your preferences a bit safer! Additionally, if you build and run the application now, you shouldn’t get the crash you previously encountered, due to security violations of Android 7+ versions. You should now enforce a secure location for your app install directory.

Limiting installation directories

One of the bigger problems Android faced in the past few years is not having enough memory to install a lot of applications. This was mostly due to lower storage capacity of devices, but since technology has advanced, and phones had become somewhat cheaper, most devices now pack plenty of storage for a plethora of apps. However, to mitigate insufficient storage, Android allows you to install apps to external storage. This worked pretty well, but over the years, a lot of security concerns have been raised around this approach. Installing applications on external SD cards is a cool way to conserve storage, but also a security flaw, since anyone with the access to the SD card also has access to the application data. And that data could hold sensitive information. This is why it’s encouraged to restrict your app to internal storage.

To do this, open the AndroidManifest.xml file and find the line that reads android:installLocation="auto" and replace it with the following:

android:installLocation="internalOnly"

Now, the install location is limited to the device, but you can still back up your app and its data. This means that users can access the contents of the app’s private data folder using adb backup. To disallow backups, find the line that reads android:allowBackup="true" and replace the value with "false".

Following these best practices, you’ve hardened your app to some extent. However, you can bypass these permission measures on a rooted device. The solution is to encrypt the data with a piece of information which potential attackers cannot find.

Securing User Data With a Password

Device lock screen

You’ll encrypt the data with a well-known recommended standard, Advanced Encryption Standard (AES). AES uses substitution–permutation network to encrypt your data with a key. Using this approach, it replaces bytes from one table with the bytes from another, and as such creates permutations of data. To begin using AES, you have to first create the encryption key, so let’s do that.

Creating a Key

As mentioned above, AES uses a key for encryption. That same key is also used to decrypt the data. This is called symmetric encryption. The key can be different lengths, but 256 bits is standard. Directly using the user’s password for encryption is dangerous. It likely won’t be random or large enough. As such the user password is different from the encryption key.

A function called Password-Based Key Derivation Function (PBKDF2) comes to the rescue. It takes a password and, by hashing it with random data many times over, it creates a key. The random data is called salt. This creates a strong and unique key, even if someone else uses the same password.

PBKDF2 diagram

Because each key is unique, if an attacker steals and publishes the key online, it doesn’t expose all the users that used the same password.

Start by generating the salt. Open up the Encryption.kt file, and add the following code to the first encrypt method, where it reads //TODO: Add code here:

val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)

Here, you use the SecureRandom class, which makes sure that the output is difficult to predict. That’s called a cryptographically strong random number generator.

Now, you’ll generate a key with the user’s password and the salt. Add the following right under the code you just added:

val pbKeySpec = PBEKeySpec(password, salt, 1324, 256) // 1
val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") // 2
val keyBytes = secretKeyFactory.generateSecret(pbKeySpec).encoded // 3
val keySpec = SecretKeySpec(keyBytes, "AES") // 4

Here’s what is going on inside that code. You:

  1. Put the salt and password into PBEKeySpec, a password-based encryption object. The constructor takes an iteration count (1324). The higher the number, the longer it would take to operate on a set of keys during a brute force attack.
  2. Passed PBEKeySpec into the SecretKeyFactory.
  3. Generated the key as a ByteArray.
  4. Wrapped the raw ByteArray into a SecretKeySpec object.

Note: For the password, most of these functions work with a CharArray instead of String objects. That’s because objects like String are immutable. A CharArray can be overwritten, which allows you to erase the sensitive information from memory after you’re done with it.