Articles Encryption Wrapper for Android SharedPreferences (Java) by DanaBr

emailx45

Местный
Регистрация
5 Май 2008
Сообщения
3,571
Реакции
2,438
Credits
573
Encryption Wrapper for Android SharedPreferences
Danabr - 27/Mar/2013
[SHOWTOGROUPS=4,20]
This article explains why and how you should protect your app's settings from prying eyes
Introduction
This article presents a wrapper class for Android's Для просмотра ссылки Войди или Зарегистрируйся interface, which adds a layer of encryption to the persistent storage and retrieval of sensitive key-value pairs of primitive data types.

Why should you care about this as an Android developer? Read on...

Background
Android's Для просмотра ссылки Войди или Зарегистрируйся interface provides a general framework that allows you to access and modify key-value pairs of primitive data types (booleans, numbers, strings, and more). This data persists across user sessions, even if your application is killed. For more information, see the Для просмотра ссылки Войди или Зарегистрируйся and the Для просмотра ссылки Войди или Зарегистрируйся.

By default, Android stores this data in an unencrypted XML file within the app's directory on the device's filesystem, with permissions that allow only the app to access this file. This is part of the concept known as "Для просмотра ссылки Войди или Зарегистрируйся". So the data is private and protected, right? Well, not quite.

If the Android device is rooted, other apps (with root privileges) can read/download/modify this file (as well as the entire filesystem). Worse still, even if the device is unrooted but attackers gain physical access to it, they might be able to download all the data from it, for example with the Для просмотра ссылки Войди или Зарегистрируйся. See the Для просмотра ссылки Войди или Зарегистрируйся for more information.

So what can you do? Encrypt the data! This way, even if attackers gain access to it, it will remain unreadable and unmodifiable. The class presented below provides an easy and generic solution for this, based on Google's recommendations in the Для просмотра ссылки Войди или Зарегистрируйся.

Using the code
As you may already know, there are 3 methods to initialize a Для просмотра ссылки Войди или Зарегистрируйся object:
The class in this article accepts a Для просмотра ссылки Войди или Зарегистрируйся object and uses the 3rd approach internally, so instead of the above you should do the following (where this is your app's Для просмотра ссылки Войди или Зарегистрируйся, Для просмотра ссылки Войди или Зарегистрируйся, etc.):
Код:
SharedPreferences prefs = SecurePreferences(this);

For now, this class does not have a constructor which accepts an existing Для просмотра ссылки Войди или Зарегистрируйся object for wrapping. The reasons for this safety measure are specified in the "Attention" section below, but future versions of this article may provide additional constructors.

By the way, the constructor also initializes the encryption/decryption key in memory.
Код:
public class SecurePreferences implements SharedPreferences {
private static SharedPreferences sFile;
private static byte[] sKey;

/**
* Constructor.
*
* @param context the caller's context
*/
public SecurePreferences(Context context) {
// Proxy design pattern
if (SecurePreferences.sFile == null) {
SecurePreferences.sFile = PreferenceManager.getDefaultSharedPreferences(context);
}
// Initialize encryption/decryption key
try {
final String key = SecurePreferences.generateAesKeyName(context);
String value = SecurePreferences.sFile.getString(key, null);
if (value == null) {
value = SecurePreferences.generateAesKeyValue();
SecurePreferences.sFile.edit().putString(key, value).commit();
}
SecurePreferences.sKey = SecurePreferences.decode(value);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
...
}

That's it! Other than that, you use this class just like a regular Для просмотра ссылки Войди или Зарегистрируйся object. The only difference is that this class transparently encrypts and decrypts the keys and the values that you provide.

For example, here is how you can get and set a string:
Код:
String value = prefs.getString("myKey", "defaultValue");
prefs.edit().putString("myKey", "myValue").commit();

And here is the implementation of these calls:
Код:
@Override
public String getString(String key, String defaultValue) {
final String encryptedValue =
SecurePreferences.sFile.getString(SecurePreferences.encrypt(key), null);
return (encryptedValue != null) ? SecurePreferences.decrypt(encryptedValue) : defaultValue;
}
@Override
public SharedPreferences.Editor putString(String key, String value) {
mEditor.putString(SecurePreferences.encrypt(key), SecurePreferences.encrypt(value));
return this;
}

Lastly, FYI:
  • This class requires API level 8 (Android 2.2, a.k.a. "Froyo") or greater
  • The example above shows strings, but all the other data types of the Для просмотра ссылки Войди или Зарегистрируйся interface are supported as well: boolean, float, int, long, and Set<String>
  • null and empty string values are not encrypted
Attention
The "Background" section above explained the "Для просмотра ссылки Войди или Зарегистрируйся" concept, which provides some privacy by default. It's important to understand the details of this default behavior, so it won't be changed inadvertently and weaken your app's security:
  1. The Для просмотра ссылки Войди или Зарегистрируйся object instance should be initialized with the Для просмотра ссылки Войди или Зарегистрируйся flag
  2. The location of the Для просмотра ссылки Войди или Зарегистрируйся object's persistent XML file should be in the device's internal storage, rather than on an SD card, since permissions aren't enforced on external storage
Also, the class presented above provides important - but nevertheless imperfect - protection against simple attacks by casual snoopers. It is crucial to remember that even encrypted data may still be susceptible to attacks by advanced attackers, especially on rooted or stolen devices!
As written by Michael Burton (a.k.a. emmby) in Для просмотра ссылки Войди или Зарегистрируйся:
Any attacker that has access to your preferences file is fairly likely to also have access to your application's binary, and therefore the keys to unencrypt the password.
Further Improvements
A suggestion from the Для просмотра ссылки Войди или Зарегистрируйся web page, in case your sensitive data is user credentials:
Where possible, username and password should not be stored on the device. Instead, perform initial authentication using the username and password supplied by the user, and then use a short-lived, service-specific authorization token.
And another suggestion from the Для просмотра ссылки Войди или Зарегистрируйся, for general sensitive data:
To provide additional protection for sensitive data, you might choose to encrypt local files using a key that is not directly accessible to the application. For example, a key can be placed in a Для просмотра ссылки Войди или Зарегистрируйся and protected with a user password that is not stored on the device.
And yet another suggestion from the Для просмотра ссылки Войди или Зарегистрируйся:
If your app needs additional encryption, a recommended approach is to require a passphase or PIN to access your application. This passphrase could be fed into PBKDF2 to generate the encryption key.
In other words, an even safer approach would be to have a secret PIN/passphrase/password which is not stored on the device at all. Either the user provides it, or a remote and secure server (which would require internet connectivity and deeper security evaluation). This secret would be converted by the app to the encryption/decryption key.

This way, even if the device falls into the wrong hands, the only way to hack the data would be with brute-force attacks.

Points of Interest

This article dealt with Android Для просмотра ссылки Войди или Зарегистрируйся. If you want to encrypt Для просмотра ссылки Войди или Зарегистрируйся, you can check out Для просмотра ссылки Войди или Зарегистрируйся.

History
  • 27 Mar 2013 - Added 2 new samples (integration with Android's PreferenceActivity and PreferenceFragment), added a few @TargetApi annotations in SecurePreferences.java, and fixed the getAll() method to ignore decryption failures in unencrypted key/value pairs
  • 25 Feb 2013 - Initial revision

License
This article, along with any associated source code and files, is licensed under Для просмотра ссылки Войди или Зарегистрируйся

[/SHOWTOGROUPS]