Android encrypted shared preferences

Secure your data with Encrypted Shared Preferences from Jetpack libraries

Jan 6 · 5 min read

Security is a very important topic and has to be taken seriously when developing Android applications, especially if sensitive data is stored on the device such as user information or API tokens. Some third party libraries already exist to secure the app data like SecurePreferences but Google introduced its own security library within the Jetpack suite. Let see how we can use it to encrypt the shared preferences of an application and do the migration from SecurePreferences.

Jetpack Security libraries overview

The Jetpack suite provi d es two security libraries to secure your app data. Security-crypto is there to encrypt files stored by your application or to secure the keysets stored the shared preferences. Security-identity-credential provides a secure store for user identity documents. For devices running Android 11 where Identity Credential is supported, the Identity Credential APIs will be used, otherwise an Android Keystore-backed implementation will be applied.

The crypto and identity libraries are supported from Android 24 and above. Starting with the version 1.1.0-alpha01, the crypto library can be used from Lollipop devices (API 21).

To use these libraries in your projects, you just need to add the following Gradle dependencies:

Under the hood, Jetpack security is using the crypto library Tink developed and maintained by a team of cryptographers and security engineers at Google. Tink is cross-platform and can be used in multiple languages (Java, C++, python…). The library provides secure APIs that are easy to use and offers several cryptographic operations such as HMAC ( hash-based message authentication code), AES-GCM ( Advanced Encryption Standard), ECDSA (Elliptic Curve Digital Signature Algorithm) or even supports envelope encryption with a Google Cloud KMS key for example.

google/tink

Tink is a multi-language, cross-platform, open source library that provides cryptographic APIs

Now that we are all set, let’s dig into the crypto library in order to secure the shared preferences of our applications.

Encrypted shared preferences

Here, we are going to use EncryptedSharedPreferences to secure the key value pairs stored in the shared preferences. You can create a new preference file when you are building your current or new application, or you can easily migrate your existing preference file to an encrypted one.

Encryption technique

By default, EncryptedSharedPreferences relies on Advanced Encryption Standard (AES) for securing the data in your shared preferences. Two kinds of AES mode of operation will be used to do so: Galois/Counter Mode (AES-GCM) and Synthetic Initialization Vector (AES-SIV) which is similar to GCM. If you want to know more about these algorithms you can check the RFC 5116 documentation from IETF. You can also provide your custom mode of operation using the Tink library.

Use EncryptedSharedPreferences

If we look closely at the class, EncryptedSharedPreferences is an implementation of SharedPreferences that allows the keys and values to be encrypted. Getting the encrypted key value pairs will be the same as it is for the common shared preferences.

You can define your EncryptedSharedPreferences instance as the following. You will have to pass the preferences file name, a context, a master key alias created thanks to a key spec that uses the AES-GCM encryption scheme ( MasterKeys.AES256_GCM_SPEC) and the encryption schemes for the preference keys and values.

Starting with the version 1.1.0-alpha01, the APIs methods have changed to support Lollipop devices. It now uses a builder to generate the master key depending on the Android version you target.

Jetpack security libraries provide only one encryption key but you can create your own key generator spec using the class KeyGenParameterSpec.Builder which is available since the Android API level 23.

The first time the file is created, you will see a very long warning in your Logcat that might be a bit scary. But do not worry, it is just there to notify that the keyset is not yet created and has to be generated by Tink.

Now that your encrypted shared preferences are settled, you can easily access the stored data or put new values as usual with SharedPreferences.

Now, let’s see how we can migrate from the deprecated SecurePreferences library to the Jetpack one.

Migrate from SecurePreferences

Before Google introduced its security libraries, Scott Alexander-Bown released a library ( Secure-preferences) to secure the shared preferences of applications. This library has been marked as deprecated by its author and he invites the users to migrate to EncryptedSharedPreferences from androidx.security. Let’s see how you can easily handle the migration.

scottyab/secure-preferences

Please use EncryptedSharedPreferences from androidx.security in preferenced to secure-preference.

If you try to create the new EncryptedSharedPreferences file using the current SecurePreferences file name, you won’t be able to access the keyset. First, SecurePreferences has to coexist with the encrypted shared preferences to copy the secure data from the deprecated library to the new encrypted file. Remember that file names must be different. Then, right after the data has been copied, you will be able to delete the old file to only use the file from EncryptedSharedPreferences .

Читайте также:  Android sdk installation что это

Once all your users are on the new version where EncryptedSharedPreferences is supported, you will be able to drop the support of Secure-preferences and remove it from your dependencies. That’s the main drawback here, the process of migrating all users to the new version can take a very long time.

Security is very important while developing applications. Everyone should think of using these libraries as much as possible to ensure that users evolve in a secure environment. Thanks to that, you will be able to easily secure your sensitive information without any hardcoded secure key in your APKs and you will avoid working directly with the Android KeyStore.

If you are already using DataStore for storing key-value pairs or typed objects, I advise you to take a look at the article “ DataStore: Security” written by Mark Allison where he shows how to secure data thanks to a Cipher.

I hope you liked this article and learnt how to secure your shared preferences. Do not hesitate to ping me on Twitter if you have any question 🤓

Big thanks to Dinorah Tovar and Merab Tato Kutalia for reviewing the article 👏🙂

Источник

Encrypting Shared Preferences with the AndroidX Security Library

Encrypting Shared Preferences with the AndroidX Security Library

The Android framework provides us with SharedPreferences , which is a great way to store a small amount of key-value data. When working with sensitive data, however, it’s important to consider that SharedPreferences stores data as plain text. We should encrypt sensitive data to keep it from prying eyes. How can we do so?

One option is to write our own encryption wrapper around SharedPreferences using the Android KeyStore. Unfortunately, that can be pretty complicated and involve a lot of setup. Another option is to use a third-party library, which means that we need to spend time finding and vetting one. Thankfully, the AndroidX Security library was recently added and makes storing encrypted shared preferences data simple and easy for apps with a min-sdk of 23+.

On to the details

To get started with the AndroidX Security library, add the following dependency to your module-level build.gradle file.

This is the latest version as of this writing. Be sure to check the library’s releases page to see if a newer version is available.

It’s important to note that this library is currently in the alpha phase. This means that while the functionality is stable, parts of the API may be changed or removed in future versions.

With the dependency added, the next step is to create an encryption master key and store it in the Android KeyStore. The security library provides us with an easy way to do this. The following code can be placed where you plan to create your EncryptedSharedPreferences instance.

We’re given a default key generation specification, AES256_GCM_SPEC , to use for creating the master key. While it’s recommended to use this specification, you can also provide your own KeyGenParameterSpec if you need more control over how the key is generated.

Finally, we just need an instance of EncryptedSharedPreferences , which is a wrapper around SharedPreferences and handles all of the encryption for us. Unlike SharedPreferences , which we can get from Context#getSharedPreferences or Activity#getPreferences , we’ll need to create our own instance of EncryptedSharedPreferences .

We provide the name for the shared preferences file, the masterKeyAlias we created earlier, and a Context . The final two arguments are the schemes with which keys and values are encrypted. They are the only options provided by the library.

Once we’ve created our EncryptedSharedPreferences instance, we can use it just like SharedPreferences to store and read values. Altogether, we have the following:

Double-checking our work

How do we know that our data is being encrypted? Let’s take a look at the contents of our shared preferences file. If you’d like to view the contents of your own shared preferences file, StackOverflow has you covered. Using the normal SharedPreferences , this is what we have:

As we can see, both the key and value are stored unencrypted. Using EncryptedSharedPreferences , our shared preferences file resembles the following:

We can see that our key-value data has been encrypted and that two keysets were stored, as well. There is one keyset for shared preference keys and another for values. Keysets contain cryptographic keys that are used to encrypt and decrypt the shared preference data. The master key we created earlier is used to encrypt these keysets so they can be stored in shared preferences along with the data they’re for.

Wrapping up

Android’s SharedPreferences is a useful tool to store key-value data, and when that data is sensitive, it’s a good idea to encrypt it. The recent AndroidX Security library is a welcome addition that provides us with a simple and easy-to-use interface to do so.

Читайте также:  Скилл бокс андроид разработчик

Источник

Android encrypted shared preferences

Armadillo — Encrypted Shared Preference

A shared preference implementation for secret data providing confidentiality, integrity and authenticity. Per default uses AES-GCM, BCrypt and HKDF as cryptographic primitives.

Important Notice: If you migrate to v0.6.0 and use a user password and default key stretching function migration is needed due to a security issue. See migration guide in the changelog for v0.6.0

  • No-Nonse State-of-the-Art Crypto: Authenticated Encryption with AES-GCM, key derivation functions Bcrypt and HKDF
  • Flexible: Tons of nobs and switches while having sane defaults
  • Modular: use your own implementation of symmetric cipher, key stretching, data obfuscation, etc.
  • Lightweight: No massive dependencies required like BouncyCastle or Facebook Conceal
  • Using it with a user provided password (and strong password hash, like the default BCrypt): your data is strongly encrypted
  • Using it without a user provided password: your data is obfuscated and cannot be easily altered or read by an attacker with access to the device
  • By using fingerprinting, it is not easily possible to just copy data over to another device and use it there
  • Encryption is non-deterministic, which means even if you encrypt the same data it appears to be different
  • All encrypted data is protected against modification by an outside attacker, so long as the encryption itself is not circumvented
  • The Android Keystore System is not used, since it proved to be unreliable and hard to handle in production due to device fragmentation and poor driver support (read more below). This implementation is a good fallback solution for a system that uses the aforementioned.

Add the following to your dependencies (add jcenter to your repositories if you haven’t)

A very minimal example

The following example shows some of the configurations available to the developer:

A xml file named like f1a4e61ffb59c6e6a3d6ceae9a20cb5726aade06.xml will be created with the resulting data looking something like that after the first put operation:

Unfortunately Android SDK 19 (KITKAT) does not fully support AES GCM mode. Therefore a backwards compatible implementation of AES using CBC with Encrypt-then-MAC can be used to support this library on older devices. This should provide the same security strength as the GCM version, however the support must be enabled manually:

In this mode, if on a KitKat device the backwards-compatible implementation is used, the default AES-GCM version otherwise. Upgrading to a newer OS version the content should still be decryptable, while newer content will then be encrypted with the AES-GCM version.

  • AES + GCM block mode: To make sure that the data is not only kept confidential, but it’s integrity also preserved, the authenticated encryption AES+GCM is used. GCM can be implemented efficiently and fast and is the usually alternative to encrypt then mac with AES+CBC and HMAC. The authentication tag is appended to the message and is 16 byte long in this implementation. A downside of GCM is the requirement to never reuse a IV with the same key, which is avoided in this lib.
  • Every put operation creates a different cipher text: Every put operation generates new salts, iv so the the resulting cipher text will be unrecognizably different even with the same underlying data. This makes it harder to check if the data actually has changed.
  • KDFs with Key Stretching features for user passwords: Add brute-force protection to possibly weak user provided passwords (e.g. BCrypt).
  • Minimum SDK 19 (Android 4.4): A way to increase security is to cap older implementation. SDK 19 seems to be a good compromise where most of the older security hack fixes are not necessary anymore, but still targeting most devices.
  • Use of JCA as Provider for cryptographic primitives: Various security frameworks exists in Java: BouncyCastle, Conscrypt, Facebook Conceal. The problem is that these libraries are usually huge and require manual updates to have all the latest security fixes. This library however depends on the default JCA provider (although the developer may choose a different one). This puts trust in the device and it’s implementation, while expecting frequent security patches. Usually the default provider since KitKat is AndroidOpenSSL provider which is fast (probably hardware accelerated for e.g. AES) and heavily used by e.g. TLS implementation.
  • Android Keystore System is not used: In my humble opinion, the Android Keystore is the best possible way to secure data on an Android device. Unfortunately, due to the massive fragmentation in the ecosystem properly handling and using the Android Keystore System is not easy and has some major drawbacks. Due to working in a security relevant field I have a lot of experience with this technology, therefore the decision was made to not support it for this implementation i.e. to keep it simple.
  • Use of data obfuscation: To disguise the actual data format and appear as a pseudo random byte array, obfuscation is used. This deliberately uses non standard ways to make it a bit harder to reverse engineer.

User provided Passwords

Читайте также:  Android fastboot flash all

A high entropy value not known to any system but the user is a good and strong base for a cryptographic key. Unfortunately user-based passwords are often weak (low-entropy). To mitigate that fact and help preventing easy brute-forcing key derivation functions with key stretching properties are used. These functions calculate pseudo-random data from it’s source material which requires mandatory work.

The following implementations are available:

  • BCrypt: based on blowfish, has a variable cpu cost parameter and a fixed memory cost parameter (default)
  • PBKDF2: applies a pseudorandom function, such as hash-based message authentication code (HMAC), to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key; no memory hardness

It is possible to provide any KDF implementation to the storage with providing a custom KeyStretchingFunction implementation.

Note, if you use key stretching put/get operations will get very slow (depeding on the work factor of course), so consider accessing the store in a background thread.

This store bases part of it’s security on so called fingerprinting. That basically means, during runtime entropy from e.g. the device, system or other parts are used to create a cryptographic key with which the data is encrypted. It basically is encryption with a semi-secret key.

This has the following benefits:

  • Binding the data to the executing runtime (ie. making it harder to lift the data and trying to read it in a different environment)
  • Strongly obfuscating the data bordering actual encryption when the used fingerprint is infeasible to guess
  • Be able to scope the data to a specific environment (e.g. when using the Android OS image build number, every update invalidates the data)

This store has a default implementation of EncryptionFingerprint which can only use generic data. In detail the following properties are incorporated:

  • Fingerprint of the APK signature
  • Android ID : a 64-bit number (expressed as a hexadecimal string) byte random value; on SDK 26 and higher, unique to each combination of app-signing key, user, and device — on SDK 25 lower only unique to user and device
  • Application package name, brand, model and name of the device
  • 32 byte hardcoded static random value

Enhancing the Strength of the Encryption Fingerprint

The security of this mechanism increases considerably if the user adds it’s own data. Here are some suggestions:

  • Random values hardcoded, locally generated or provided by a remote service
  • Unique user-id (if the application has the concept of login)
  • Device Serial (requires dangerous permission SDK > 25)
  • Sim-ID/ICCID (if changing the sim should/can invalidate the data)
  • Android OS image build fingerprint (if you want to invalidate the data after OS update)

Key Derivation Process

The cryptographic key used to encrypt the data is composed of the following parts:

  • User password (optional): provided by the caller and stretched with e.g. Bcrypt
  • Encryption Fingerprint (see section above)
  • Entry Key: the hashed version of the key passed by the caller; this will bind the data to that specific entry key
  • Entry Salt: a random 16 byte value unique to that specific entry that will be created on every put operation (will also be used for the key stretching function)
  • Storage Salt: a random 32 byte value unique to that specific storage, created on first creation of the storage

The concatenated key material will be derived and stretched to the desired length with HKDF derivation function.

The key is hashed with HKDF (which uses Hmac with Sha512 internally) expanded to a 20 byte hash which will be encoded with base16 (hex). The key generation is salted by the encryption fingerprint, so different shared preferences will generate different hashes for the same keys.

The diagram below illustrates the used data format. To disguise the format a little bit it will be obfuscated by a simple xor cipher.

The resulting data will be encoded with base64 and looks like this in the shared preferences xml:

All tags and commits by me are signed with git with my private key:

Assemble the lib with the following command

The .aar files can then be found in /armadillo/build/outputs/aar folder

  • Google’s Android Jetpack EncryptedSharedPreferences using Android Keystore System
  • secure-preferences using AES-CBC by @scottyab (not maintained)
  • secure-preferences supporting Android Keystore System by @ophio
  • secure-preferences using FB Conceal framework by @KaKaVip
  • secure-preferences supporting Android Keystore System by @adorsys
  • secure-preferences supporting Android Keystore System by @iamMehedi
  • secure-preferences using AES-CBC by @PDDStudio

Copyright 2017 Patrick Favre-Bulle

Licensed under the Apache License, Version 2.0 (the «License»); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an «AS IS» BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

A shared preference implementation for confidential data in Android. Per default uses AES-GCM, BCrypt and HKDF as cryptographic primitives. Uses the concept of device fingerprinting combined with optional user provided passwords and strong password hashes.

Источник

Оцените статью