Encryption Tutorial For Android: Getting Started
- Getting Started
- Securing the foundations
- Using Permissions
- Limiting installation directories
- Securing User Data With a Password
- Creating a Key
- Adding an Initialization Vector
- Encrypting the Data
- Decrypting the Data
- Saving Encrypted Data
- Securing the SharedPreferences
- Using a Key From a Server
- Using the KeyStore
- Generating a New Random Key
- Encrypting the Data
- Decrypting to a Byte Array
- Testing the Example
- Where to Go From Here?
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.
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:
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.
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_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:
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
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
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.
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)
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:
- 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.
- Generated the key as a
- Wrapped the raw
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.