AndroidX PreferenceFragmentCompat scaffolded SharedPreferences encrypted with Jetpack Security
Aug 16, 2020
Let me start by disadvising anybody from developing their own encryption algorithm, encryption protocol, or encryption schemes while trying to increase security of an application. Always use well known encryption libraries and such best practice protocols and techniques wich are advised by experts. Otherwise you can easily make a tiny mistake which could compromise your whole scheme.
This is why I’m so happy that Android Jetpack has a Security feature set which offers encryption / decryption of SharedPreferences and file streams. The fact that it’s developed by Google engineers gives a peace of mind that it’ll pass a high standard of quality and since it’s backed by Google it is safe to rely on it. The default advised usage uses latest algorithms and modes like AES256, GCM and AES-GCM-SIV. Complex scenarios can be configured using KeyGenParameterSpec, key authentication expiration can be specified and JetSec is also capable of using Titan security module which elevates the security to hardware token level (Titan basically integrates the hardware security token into the phone).
Be careful when reading blogs about JetSec because it’s still in alpha phase so the API can still change. Good example for that is the MasterKey: MasterKeys (plural) is deprecated now, so if you read stuff like MasterKeys.getOrCreate that’s an older alpha version. You’d want to look for MasterKey.Builder. This blog post won’t talk about file encryption, I want to focus on SharedPreferences encryption and specifically Kotlin - which is now the primary first class over Java citizen in the Android ecosystem. Here is a code snippet how you can easily obtain an encrypted SharedPreferences instance:
Once you have that it can be used just as if it would be a regular SharedPreferences. Once you’ve changed your code the saved cleartext data won’t be compatible with the new scheme - this is not a surprise. I have to talk about PreferenceFragmentCompat though: another basic thumb rule (besides do not write your own encryption) is to avoid plumbing code by all means. Less plumbing code means less maintenance and smaller bug surface area. Android Jetpack provides a really nice declarative way of defining your Preferences UI. That does all the weight lifting for you but it works with a non encrypted SharedPreferences by default. Even if you take a peek at the decompiled classes you may not see how JetSec encryption can be introduced. Here is a five step guide where only the last two steps have encryption related extras.
Add implementation “androidx.security:security-crypto-ktx:1.1.0-alpha02” for JetSec to you gradle.build file.
You need to add implementation “androidx.preference:preference:1.1.1” to you gradle.build file for PreferenceFragmentCompat and additional Jetpack preferences helper functionality.
Define your preferences UI in a declarative way in res/xml/preferences.xml file.
Add a small SettingsActivity stub.
Add a small SettingsFragment stub.
Supply the EncryptedPreferenceDataStore which provides the encrypted provider for preferences data.
Here is a snippet of the res/xml/preferences.xml:
The most important code in the last snippet. The only distinction between the encrypted and the non encrypted variation is the preferenceManager.preferenceDataStore = EncryptedPreferenceDataStore.getInstance(requireContext()) line. By supplying the PreferenceDataStore we can influence the underlying logic to use JetSec encryption. Here is the EncryptedPreferenceDataStore: