Smartphones

Adding a measure of security to Android's shared preferences

It falls on our shoulders as responsible Android app developers to decide which settings we allow the user to store in the shared preferences file, and, when appropriate implement our own encryption/decryption algorithms.

If you’ve released an application to Google’s Android market place, and that application required your user type in any sort of credentials, chances are your application was available for all of about twelve minutes before you received your first user request to allow those credentials be saved on the phone. Users hate to type a password every time they want to use your application. Security risk or not, at some point you may have to let some users store sensitive preferences, or chances are they just won’t use your application.

On Apple’s iOS platform, application preferences are kept in something called the key chain. Data items stored in the key chain are encrypted automatically by the operating system. Android’s preferred mechanism for keeping track of user preferences however, the SharedPreferences class, is not as security savvy. In fact, while the SharedPreferences class is straightforward and easy for a developer to use, on a rooted device it becomes obvious it simply stores the data in an XML file.

What this means is that it falls on our shoulders as responsible Android app developers to decide which settings we allow the user to store in the shared preferences file, and, when appropriate implement our own encryption/decryption algorithms. The purpose of this article is not to get you up to speed on data encryption. That’s a broad subject and there are plenty of books available on cipher theory.

Instead I’d like to share with you an extended version of the standard EditText widget, which allows you the developer to apply whatever level of encryption you feel is appropriate for your application. The extended widget has proved a handy way for me to apply a measure of security to any user preferences I choose, without requiring me to constantly deal with encoding/decoding of the strings. Furthermore, if at some point in the future I decide beefier security ciphers are required, I only have to change out the encryption mechanism in the widget, instead of refactoring the entire application.

Let’s take a look shall we?

  1. Create a new Android project (target whichever SDK you prefer).
  2. In our /src file let’s add a new class that extends android.widget.EditText.

MyEditText.Java
public class MyEditText extends EditText{
 
public MyEditText(Context context, AttributeSet attrs) {
  super(context, attrs);
}
}

Gotcha! In order to reference an extended widget from your XML layout file, you need to override this particular constructor signature. While you can override the other two signatures as well, failing to override the context/ attribute signature will result in a force close any time you load an xml layout resource that references MyEditText.

3. Besides overriding the constructor, we will want to implement a custom version of the standard widget get/set text functions. These clone the signature of the standard get/set methods, but take one additional parameter, a flag indicating whether encryption or decryption is appropriate.

public void setText(CharSequence text, boolean doDecrypt)
{
 if (doDecrypt) {
 text = rot13Decode(text);
 }
 super.setText(text); 
}
 
public Editable getText(boolean doEncrypt)
{
 Editable e = super.getText();
 if (doEncrypt) {
 e = rot13Encode(e);
 }
 return e; 
}

If you’ll notice when the flag is set I’m calling a rot13 encode/decode cipher. This is just something simple I whipped up for this demo. You need to determine what level of security is appropriate for your application. If you are unsure, I suggest using the BouncyCastle libraries available as part of the standard Android build as these provide a number of suitable ciphers.

public Editable rot13Encode(CharSequence input) {
 StringBuilder output = new StringBuilder();
 for (int i=0; i< input.length(); i++) {
  char c = input.charAt(i);
  if ((c < 32) || (c > 126)) {
  //outside our printable range so leave in tact
  output.append(c);
  } else {
  //shift
  c+=13;
  if (c > 126) {
  //wrap
  c-=((126 - 32) + 1);
  }
  output.append(c);
  }
 }
 return new SpannableStringBuilder(output);
}
 
public CharSequence rot13Decode(CharSequence input) {
 StringBuilder output = new StringBuilder();
 for (int i=0; i< input.length(); i++) {
 char c = input.charAt(i);
 if ((c < 32) || (c > 126)) {
 //outside our printable range so leave in tact
 output.append(c);
 } else {
 //shift
 c-=13;
 if (c < 32) {
 //wrap
 c+=((126 - 32) + 1);
 }
 output.append(c);
 }
 }
 return output.toString();
}

Gotcha! Notice that the encode function returns a type of Editable? This is an interface, not an actual variable type. Google’s documentation does a fair job of explaining how to convert an Editable to a String, but it really takes some digging to figure out how to go from a String to a an Editable. Creating a new SpannableStringBuilder instance from an existing CharSequence seems to do the trick quite handily.

4. Now that we can add our extended widget to our XML file, let’s create the layout for our demo in the /res/layout folder.

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:padding="4dip">
<TextView 
   android:layout_width="fill_parent" 
   android:layout_height="wrap_content"

android:layout_marginBottom="20dip"

android:gravity="center"

android:text="@string/hello"/>

<TextView 
   android:layout_width="wrap_content" 
   android:layout_height="wrap_content" 
   android:layout_gravity="center"
   android:text="@string/prompt"/>
 <com.authorwjf.MyEditText
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:layout_marginBottom="20dip"
 android:id="@+id/entry_field"/>
 <Button 
   android:layout_width="fill_parent" 
   android:layout_height="wrap_content" 
   android:id="@+id/save"
   android:text="@string/save_pref"/>
  <Button 
  android:layout_width="fill_parent" 
   android:layout_height="wrap_content" 
   android:id="@+id/retrieve"
   android:layout_marginBottom="20dip"
   android:text="@string/retrieve_pref"/>
  <CheckBox 
android:id="@+id/showme"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:checked="true"
 android:layout_gravity="center"
 android:text="@string/show_text"/>
</LinearLayout>

5. We’ll add a couple default prompts to our res/values folder.

strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
 <string name="hello">Hide Password Demo</string>
 <string name="app_name">HideMe</string>
 <string name="save_pref">Save Preference</string>
 <string name="retrieve_pref">Retrieve Preference</string>
 <string name="prompt">Type Some Text Below:</string>
 <string name="show_text">Show Password While Typing</string>
</resources>

6. Now we are ready for our main activity.

Main.Java
public class Main 
extends Activity 
implements OnCheckedChangeListener, OnClickListener
{
 //main
}

7. In the onCreate method we just need to hook our UI elements.

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  CheckBox cb = (CheckBox)findViewById(R.id.showme); //show while typing
  cb.setOnCheckedChangeListener(this);
  Button sb = (Button)findViewById(R.id.save); //save to preferences
  sb.setOnClickListener(this);
  Button rb = (Button)findViewById(R.id.retrieve); //get from preferences
  rb.setOnClickListener(this);
}

8. For the purposes of the demo, I’ve added a check box that allows you to show or not show what is being typed in our edit field. This is a pretty nifty feature built into the Android edit text widget called a transformation.

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
 MyEditText et = (MyEditText) findViewById(R.id.entry_field);
 if (isChecked) {
 et.setTransformationMethod(null);
 } else {
 et.setTransformationMethod(new PasswordTransformationMethod());
 }
}

9. And last but not least we have our button clicks. By simply passing in our flag when we get and set the text in MyEditText we can get plain or encrypted text. The toast message provides a visual indicator of what is actually saved to and retrieved from the preferences file on the device.

@Override
public void onClick(View v) {
 MyEditText et = (MyEditText) findViewById(R.id.entry_field);
 SharedPreferences settings = getSharedPreferences("MyPreferences", 0);
 if (v.getId()==R.id.save) {
  String encodedPassword = et.getText(true).toString();
 SharedPreferences.Editor editor = settings.edit();
   editor.putString("password", encodedPassword);
   editor.commit();
   Toast.makeText(Main.this, encodedPassword, Toast.LENGTH_LONG).show();
 } else {
 String encodedPassword = settings.getString("password", "");
   et.setText(encodedPassword,true);
   Toast.makeText(Main.this, encodedPassword, Toast.LENGTH_LONG).show();
 }
}

And there you have it—a convenient, flexible mechanism for adding some needed security to your own preference files. If you’d like to download the entire project, the source is available here. Enjoy!

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

1 comments
droidee
droidee

The source code zip is 0 bytes... Is there another place to look? thanks

Editor's Picks