There have been several Google security projects but this is the first Android-specific security library that’s part of AndroidX. It’s been a long time coming but I for one am happy to see it. AndroidX security is under alpha and so the feature set may yet change, but currently its primary focus is keeping data secure at rest and its use is recommended in the Android Security best practices. Files and data within your applications’ private data directory are protected by system Linux style user permissions. This prevents access from other applications or would be attackers on non-rooted devices. However it’s still good security practice to encryption your sensitive app data.
To help developers secure their app’s data at rest it offers two utilities:
- EncryptedSharedPreferences – automatically encrypts keys and values and adheres to the SharedPreference Interface. It’s easy to replace existing SharedPreference usages with this implementation instead. The rest of the article focuses on EncryptedSharedPreferences but many of the points are valid for EncryptedFile.
- EncryptedFile – allows you to read/write encrypted files by providing custom implementations of FileInputStream and FileOutputStream.
Having used powerful encryption libraries such as javax.security and BouncyCastle – both of which offer the ability to perform secure cryptographic operations but the learning curve and number of customisation options are high. This either puts people off altogether or leads to accidental but critical security mistakes. For example, I accidently used the default Initiation Vector which turned out to be a static 0 byte array – this is bad from a security point of view. I like the fact AndroidX has a very simple API and defaults to a recommended* AES GCM 256bit based encryption algorithm.
* A note on my recommendations: this is based on my understanding at the time of publishing but please do your own research to confirm this library and encryption algorithm are indeed safe and secure.
How to include in your project?
As with any of the AndroidX packages just include this dependency in your build.gradle file.
Note: This is only available within the AndroidX library suite and as such, there is no backports that are compatible with the old Android Support libraries.
Example using `EncryptedSharedPreferences`
Let’s dig into the EncryptedSharedPreferences part of library and storing arbitrary data on or about users is a common task for many applications.
MasterKeys is a helper class that generates your encryption keys and stores them in the Android Keystore.
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
Note: if you require the strongest security using a hardware-backed Keystore you can use your own KeyGenParameterSpec and alter the options via the KeyGenParameterSpec.Builder.
Create EncryptedSharedPreferences like this:
val sharedPrefs = EncryptedSharedPreferences .create( FILENAME, masterKeyAlias, context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
You can interact with SharedPreferences as normal – the encryption and decryption happens transparently to you. The masterKeyAlias was created in the previous step and is a String used to identify the key in the Android Keystore.
Under the hood
Here’s what a EncryptedSharedPreferences file looks like. This was extracted from one of my sample apps using Facebook’s Stetho library, although you could just as easily use ADB or Device File Explorer within Android Studio to pull the SharedPreferences XML file from your app’s private data directory.
As noted in the key management section of the security best practises, AndroidX Security uses a 2-part system for key management.
A keyset that contains one or more keys to encrypt a file or shared preferences data. The keyset itself is stored in SharedPreferences.
A master key that encrypts all keysets. This key is stored using the Android Keystore system.
EncryptedSharedPreferences uses two different encryption schemes. The one for the keys is deterministic, by that I mean any key (i.e “access_token”) will render to the same ciphertext every time whereas values are encrypted with a non-deterministic schema. Non-deterministic uses random data with the encryption algorithm for the same text it will generate a different ciphertext. This improved security wouldn’t work for the SharedPreference keys as they need to be consistent in order to look up the values in the HashMap.
The library is relatively new and is in Alpha02. It seems robust in my testing, as have other alpha AndroidX libraries. But it’s certainly a risk worth considering before implementing in production.
To use this library you’ll need to set your minSkdVersion to 23 (Android 6.0). This is mainly due to dependency on the Google encryption library Tink which requires SDK23 for Android Keystore operations. If this security library alone isn’t enough to increase your applications minSDK then you could opt to support AndroidX Security library for SDK 23+ and default to the regular File or SharedPreferences for users on older devices. Why reduce security of all of your users just to support old devices?
You won’t be able to use Preference Activity/Fragment with XML to link directly to the SharedPreference data so before jumping in and encrypting all the things, consider that you’re unlikely to need to encrypt all your app’s shared preferences. I’d recommend splitting out just sensitive data such as OAuth access and refresh tokens, email or any other personally identifiable data.
It’s also worth thinking about the recovery strategy in case the decrypt fails. The use case I’ve often used this type of encryption for is caching server-side data for offline access. If the key fails to be retrieved or for whatever reason, the decrypt fails. The worst case is asking the user to re-authenticate and re-download the data when they next have connectivity. Of course, this recovery strategy of starting again relies on the data being on the server and although it’s not a great user experience, I don’t see it as the end of the world (i.e 1-star rating 😉 ). However, that’s not the case for an offline-only password manager where there would be no recovery option. So remember to catch the SecurityException and handle according to your specific app/use case.
Should you use it?
“It depends” – it’s very much down to your application’s use-cases and the type of data you’re storing. For me, it’s a yes. It’s better than using nothing and from a developer point of view, it’s easy to implement and helps keep the app and user data safer. While it doesn’t offer 100% security (spoiler alert: nothing does) it will be more laborious and increase the required skill level for an attacker to successfully extract data.
If you’re already using one of the many open-source encrypted Shared Preferences libraries then I think its would be a good switch. Certainly, if you’re using SecurePreferences, a library I developed several years back, I’d urge you to migrate to AndroidX Security at this point. As mentioned earlier, the API is simpler, uses AES GCM 256 and is supported by Google rather than being neglected by me ;).
If you’re using AndroidX security in development or production I’m keen to hear you’ve experiences.