An Android app sometimes needs to store sensitive data locally. This is usually done by encrypting this type of data in order to protect it before it is misused. Encrypted data is accessible or readable only to those who have the right encryption key.
Android engineers often use the Android Keystore to encrypt sensitive information, which is saved in an encrypted form in the shared preferences file.This approach becomes complicated when an app works with structured data. Moreover, the Android Keystore is unstable storage, and the app’s data can get lost in certain scenarios, which is why I prefer using an encrypted database for storing structured-sensitive data.
I would like to share the idea of using the Android Keystore together with an encrypted Realm database.
The basics of Realm encryption
The basic concept of Realm encryption is that you use a 512-bit encryption key (64 bytes) to encrypt Realm storage. This key is often a hash based on the user’s secret password. The app can then access an encrypted database by using this unique hash. This hash is the key to the Realm and cannot be stored unprotected on the device. This means that the user of the app has to type his secret password every time the app initializes the database. How can we engineers make this process more seamless for the user?
Let’s use the Android Keystore with the well-known Android lock screen (Fingerprint/PIN/Password/Gesture) to hold the Realm key.
Encrypting a database
An app needs to compose a Realm key based on the user’s secret password in order to encrypt the database. The app will also need to store this Realm key in a protected way for later use. Then the app can use the Android Keystore to generate and protect the cryptographic key pair in order to encrypt/decrypt the original Realm key. The encrypted Realm key can be saved to shared preferences for later use.
Encrypting will require the user of the app to type in one secret password and then use the Android lock screen.
Decrypting / accessing an encrypted database
To unlock the database, the app needs to obtain and decrypt the Realm key from the Keystore and apply it.
The app needs to access the cryptographic key pair in the Keystore, read the encrypted Realm key from shared preferences and use the mentioned cryptographic key pair to decrypt the Realm key. This allows the app to use the decrypted Realm key in order to unlock and access the encrypted database.
Decrypting will require the user to use the Android lock screen.
There is a specific bug in the Android Keystore system. The whole keystore can be deleted under certain scenarios. Therefore, this bug affects the advanced technique described above. The app should have a backup solution. If the app tries to decrypt the encrypted Realm key, but the Keystore was accidentally deleted, then the app will need to compose the Realm key again.
The user of the app will be asked to input his secret password again. After doing so, the app will repeat the database encryption process.
Common questions include: “What if the user loses his phone?” “Is the Keystore safe enough?” These are tricky to answer. A skilled hacker could break the Android Keystore on a rooted device. But there is good news: an engineer can implement a root detection (e.g. RootBeer) in the app. Any sign of root detection would delete the app’s keystore and block any further use of the app.
Engineers can also use SafetyNet for advance detection techniques (not only root detection) to protect local sensitive information before it is misused on the rooted device.This will significantly decrease the chance of getting hacked.
Supported Android API
An app should use at least API 19 (4.4 KitKat) in order to safely access the Android Keystore. But the recommended version is API 23+ (6 Marshmallow and above).
Let’s look closely at Android Keystore-supported APIs.
API 18 - The Android Keystore System was formally introduced in this Android version. Due to stack-based buffer overflow vulnerability (fixed for API19+), it is safer to use the Android Keystore starting with Android KitKat / API 19.
API 19 - This Android version is still missing proper lock-screen implementation. But it is possible to force lock if app requests Device Administration rights.
API 21 - This is the first Android version with native support of lock screen. But forcing the screen to lock is still not defined in the certificate itself. This still needs to be explicitly handled.
API 23 - Since the introduction of Marshmallow, an engineer can define the lock-screen requirement directly in the Android Keystore’s cryptographic key pair. Marshmallow also includes AES and HMAC cipher support, the addition of an access control system for a hardware-backed keystore and lots of other security options (23+). The cryptographic key pair generation is completely new. Marshmallow also started supporting fingerprint authentication in this version.
Encrypting a long key
Realm requires a 512-bit long key. It’s true that encrypting a 512-bit long key with RSA in the Android Keystore can be tough. The ideal way to encrypt a long key is by using AES.
If the Android Keystore only supports the RSA (on KitKat or Lollipop), an engineer might try to solve this situation with the following trick: Generate and save the 256-bit long key to the Android Keystore only. For Realm, just double this key size to 512-bit.
I wouldn’t recommend saving/encrypting a plain password (instead of a hash) in the Keystore directly. An engineer can avoid complications with encryption of the long key by encrypting just the plain password, but this could compromise a user’s password if the Keystore is hacked.
No code samples in this post
In this post, I tried to describe the use of Android Keystore & Realm without detailing all the implementation steps to keep the principle clear. For those who want further information about the implementation, the DB SHOWCASE ANDROID app is available on GitHub. You can try this app on Google Play. All the necessary code to implement Keystore is then extracted to the KEYSTORE COMPAT wrapper library, which is available on GitHub or Bintray. DB SHOWCASE works with API 16 and above. DB SHOWCASE allows a user to work with the Android Keystore when you install it on Android API 19 or above. This functionality will be disabled on lower APIs.
More about encrypted Realm in sample app
The app works with unencrypted Realm storage by default. A user can use the CONTROL screen with the Android security switch to encrypt Realm. Encryption-related code is available on Github in ControlRootViewModel.java.
The stored Realm key has to be loaded every time the app initializes encrypted database. For more details about loading the Realm key from the Keystore look at ShowcaseRealmModule.java.initConfig().
Every loading of the Realm key (which accesses the Android Keystore) in the sample app requires the lock screen. More details about how the app handles the lock-screen result (cancel or success) is available in MainViewMode.java.onViewAttached() and MainActivity.java.onActivityResult().
Feel free to use any part of the code or ideas mentioned in this post.