What is the most appropriate way to store user settings in Android application
Asked Answered
M

16

322

I am creating an application which connects to the server using username/password and I would like to enable the option "Save password" so the user wouldn't have to type the password each time the application starts.

I was trying to do it with Shared Preferences but am not sure if this is the best solution.

I would appreciate any suggestion on how to store user values/settings in Android application.

Muddler answered 24/4, 2009 at 14:3 Comment(0)
R
241

In general SharedPreferences are your best bet for storing preferences, so in general I'd recommend that approach for saving application and user settings.

The only area of concern here is what you're saving. Passwords are always a tricky thing to store, and I'd be particularly wary of storing them as clear text. The Android architecture is such that your application's SharedPreferences are sandboxed to prevent other applications from being able to access the values so there's some security there, but physical access to a phone could potentially allow access to the values.

If possible I'd consider modifying the server to use a negotiated token for providing access, something like OAuth. Alternatively you may need to construct some sort of cryptographic store, though that's non-trivial. At the very least, make sure you're encrypting the password before writing it to disk.

Russom answered 24/4, 2009 at 16:20 Comment(9)
Could you please explain what you mean by sandboxed?Bryner
a sandboxed program is any application whose process and information (such as those shared preferences) remains hidden from the rest of the applications. An android application running in a package cannot directly access to anything inside another package. That's why applications in the same package (which are always yours) could access to information from other onesGeosphere
@Reto Meier my requirement is to protect the publicly available web services for that I am using a token, is storing it on shared preferences is safe? i have a bootup broadcast receiver in my application which will delete all sharedpreferences data if it found device as rooted. Is this enough to protect my token.Countercheck
Per android-developers.blogspot.com/2013/02/…, User credentials should be stored with the MODE_PRIVATE flag set and stored in internal storage (with the same caveats about storing any sort of password locally ultimately open to attack). That said, is using MODE_PRIVATE with SharedPreferences equivalent to doing the same with a file created on internal storage, in terms of effectiveness to obfuscate locally stored data?Infantryman
Do not store a password in shared preferences. If the user ever loses the phone, they've lost the password. It will be read. If they used that password elsewhere, everyplace they used it is compromised. In addition, you've permanently lost this account because with the password they can change your password. The correct way to do this is to send the password up to the server once, and receive a login token back. Store that in shared preference and send it up with each request. If that token is compromised, nothing else is lost.Konikow
Speaking of storing the credentials safely.Read this https://mcmap.net/q/100957/-how-to-securely-store-credentials-password-in-android-applicationThigmotropism
@reto-meier "At the very least, make sure you're encrypting the password before writing it to disk." How do you access or store the encryption key in that case? If you have to retrieve the key from the server every time, how can you possibly retrieve it (in a secure way) without requiring the user to log in interactively (which defeats the purpose of remembering credentials to avoid login). How do you prevent a rooted device to heap dump your application and finding the encryption key?Increasing
@Reto Meier I store the user's email & password encrypted in shared preferences, is there a place where I can put my key? I'm searching for something like a password/keymanager: The user unlocks his phone with his pin -> the pin gets used to automatically decrypt the keymanager -> all apps can access their own keys now to decrypt their shared preference values. -> when the phone gets locked, the keymanager gets encrypted automatically. Now my shared preferences would be safe, even when the device gets rooted, no? I'm new to app development, but if something like this doesn't exist, why not?Palaeography
I know a 4 char pin is not safe against brute attacks, but many phones use fingerprint or face recognition. You could tell the users, if they want to keep using a pin they should consider using a longer one. I mean even with a 4 char pin it would be at least an additional security measure at zero loss to UX.Palaeography
S
213

I agree with Reto and fiXedd. Objectively speaking it doesn't make a lot of sense investing significant time and effort into encrypting passwords in SharedPreferences since 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.

However, that being said, there does seem to be a publicity initiative going on identifying mobile applications that store their passwords in cleartext in SharedPreferences and shining unfavorable light on those applications. See http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ and http://viaforensics.com/appwatchdog for some examples.

While we need more attention paid to security in general, I would argue that this sort of attention on this one particular issue doesn't actually significantly increase our overall security. However, perceptions being as they are, here's a solution to encrypt the data you place in SharedPreferences.

Simply wrap your own SharedPreferences object in this one, and any data you read/write will be automatically encrypted and decrypted. eg.

final SharedPreferences prefs = new ObscuredSharedPreferences( 
    this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE) );

// eg.    
prefs.edit().putString("foo","bar").commit();
prefs.getString("foo", null);

Here's the code for the class:

/**
 * Warning, this gives a false sense of security.  If an attacker has enough access to
 * acquire your password store, then he almost certainly has enough access to acquire your
 * source binary and figure out your encryption key.  However, it will prevent casual
 * investigators from acquiring passwords, and thereby may prevent undesired negative
 * publicity.
 */
public class ObscuredSharedPreferences implements SharedPreferences {
    protected static final String UTF8 = "utf-8";
    private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
                                               // Don't use anything you wouldn't want to
                                               // get out there if someone decompiled
                                               // your app.


    protected SharedPreferences delegate;
    protected Context context;

    public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
        this.delegate = delegate;
        this.context = context;
    }

    public class Editor implements SharedPreferences.Editor {
        protected SharedPreferences.Editor delegate;

        public Editor() {
            this.delegate = ObscuredSharedPreferences.this.delegate.edit();                    
        }

        @Override
        public Editor putBoolean(String key, boolean value) {
            delegate.putString(key, encrypt(Boolean.toString(value)));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            delegate.putString(key, encrypt(Float.toString(value)));
            return this;
        }

        @Override
        public Editor putInt(String key, int value) {
            delegate.putString(key, encrypt(Integer.toString(value)));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            delegate.putString(key, encrypt(Long.toString(value)));
            return this;
        }

        @Override
        public Editor putString(String key, String value) {
            delegate.putString(key, encrypt(value));
            return this;
        }

        @Override
        public void apply() {
            delegate.apply();
        }

        @Override
        public Editor clear() {
            delegate.clear();
            return this;
        }

        @Override
        public boolean commit() {
            return delegate.commit();
        }

        @Override
        public Editor remove(String s) {
            delegate.remove(s);
            return this;
        }
    }

    public Editor edit() {
        return new Editor();
    }


    @Override
    public Map<String, ?> getAll() {
        throw new UnsupportedOperationException(); // left as an exercise to the reader
    }

    @Override
    public boolean getBoolean(String key, boolean defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
    }

    @Override
    public float getFloat(String key, float defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
    }

    @Override
    public int getInt(String key, int defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
    }

    @Override
    public long getLong(String key, long defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Long.parseLong(decrypt(v)) : defValue;
    }

    @Override
    public String getString(String key, String defValue) {
        final String v = delegate.getString(key, null);
        return v != null ? decrypt(v) : defValue;
    }

    @Override
    public boolean contains(String s) {
        return delegate.contains(s);
    }

    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }




    protected String encrypt( String value ) {

        try {
            final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);

        } catch( Exception e ) {
            throw new RuntimeException(e);
        }

    }

    protected String decrypt(String value){
        try {
            final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(pbeCipher.doFinal(bytes),UTF8);

        } catch( Exception e) {
            throw new RuntimeException(e);
        }
    }

}
Sophronia answered 18/6, 2011 at 2:42 Comment(24)
FYI Base64 is available in API level 8 (2.2) and later. You can use iharder.sourceforge.net/current/java/base64 or something else for earlier OSs.Sophronia
And another os version-related comment: some devices return null for ANDROID_ID on os 2.1 or earlier, so you'll want to use a known value in those situations. Something like "9774d56d682e549c", which is the android emulator id, might work.Sophronia
Or generate your own ID: android-developers.blogspot.com/2011/03/…Marmoreal
@Sophronia Is this code that you have created? If yes is it okay to use it in a closed source app? If you want attribution for the code how do you want to be attributed?Adrenal
Yes, I wrote this. Feel free to use, no attribution necessarySophronia
I agree with you. But if the password is only used on the server, why not use Public/private key encryption? Public key on client when saving the password. The client will never have to read the clear text password again, right? The server can then decrypt it with the private key. So even if somebody goes through your app source code, they can't get the password, except they hack your server and get the private key.Evyn
Great contribution. I'd like to add that Android warns against inner classes having making reference to their creators (potential leak). Try declaring Editor static, and create a WeakReference to the outer class to call encrypt/decrypt. Also: require the constructor for Editor to take the outer class for assignment in constructor. That should alleviate any concerns with leaks. Although I am a Java newb...feel free to criticize.Cardiganshire
Get: Caused by: javax.crypto.IllegalBlockSizeException: last block incomplete in decryption?Toodleoo
I'm a bit confused. The first paragraph states not to encrypt password as access to the binary gives potential access to the key, yet the example answer is to encrypt the password using a key?Condiment
I've just dumped this into my android project, but it complains that DEVICE_ID is deprecated. Is this still ok to use?Indiscrimination
You should use now Settings.Secure.ANDROID_ID, according to this: developer.android.com/reference/android/provider/…Loup
For that users that are starting and dont understand Settings.Secure.Android_ID just change that line: pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.System.ANDROID_ID).getBytes(UTF8), 20)); for that one: pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec( Secure.getString(context.getContentResolver(), Secure.ANDROID_ID).getBytes(UTF8), 20));Spruik
I've added a few features to this code and placed it on github at github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/…. It now handles migrating a non-encrypted preferences to the encrypted one. Also it generates the key at runtime, so decompiling the app does not release the key.Indebted
I dont understand the logic in this implication: "...have access to your application's binary, and therefore the keys to unencrypt the password.". How you explain here p --> q ?Accommodating
@sports: Having the binaries means that they can decompile your app and then the keys stored in code are visible to the attacker. They can use these keys to unencrypt the password information.Curassow
@DerekW you make "decompiling" to look like it is a trivial, easy and even possible thing. Do you have a proof of this? ie: "prove that given any binaries, you can decompile sourcecode." ..so this means that I can decompile WhatsApp (and any other closedsource application) and check out the sourcecode? Or by decompiling I can only obtain hardcoded values used by the rest of teh code?Accommodating
@sports: Trivial is a relative term, but the best you can truly do is make the would-be hacker work for it. Here's a question that more fully addresses your question: #13854925Curassow
Bump I guess. But with this implementation against sdk19 I get a thrown exception (InvalidAlgorithmParameterException) when encrypting.Trotskyite
Late addition, but the comment by @PatrickBoos is a great idea. One problem with this, though, is that even though you've encrypted the password, an attacker that stole that cipher would still be able to log in to your servers, because your servers do the decryption. One addition to this approach is to encrypt the password together with a timestamp. That way you can decide, for example, to only allow passwords saved in the recent past (like adding an expiration date to your "token"), or even requiring certain users to have a timestamp since a particular date (let's you "revoke" old "tokens").Boulevard
FYI as of Android 4.0.1, there are a couple of new methods on SharedPreferences you need to overrideMulvey
for a certain attack vector, a simple reverse engineering would do the trick and get your decryption key.Puerility
Hi @emmby, thanks for great answer, i've used your code in my application, but i've got following error in decrypt method ( javax.crypto.BadPaddingException: pad block corrupted). I searched error but i don't find any solution. can you help me on this error ? i don't get this error all time, 3 user crash from 3k.Brueghel
Regarding the password problem, "encrypting" sharedprefs isn't the solution, a better way is to store a hash of a salted password instead, so that the original password cannot be guessed if it's compromised (good given that many people reuse their passwords). It's good that publicity is shed on these issues and there are already best practices out there beyond the scope of this question to manage credentials on any system.Incogitable
what is the most secure way to set SEKRIT field - compile time, from some user input or totally randomly generate at install time and then somehow store it in the preferences for future use?Foregoing
L
29

About the simplest way to store a single preference in an Android Activity is to do something like this:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

If you're worried about the security of these then you could always encrypt the password before storing it.

Lavish answered 4/5, 2009 at 8:50 Comment(4)
I couldn't agree with you more about this simplistic approach; however, you should always be worried about the security of passwords that you store? Depending on your application, you have potential liabilities for stolen personal information. Just pointing this out for anybody trying to store actual passwords to such things as bank accounts or something equally important. I still vote you though.Ribwort
Where would you store the key that stored the password? If the shared preferences are accessible by other users, so is the key.Prussian
@Prussian did you get the answer.?Spongy
@Spongy The answer is there is no answer. EnryptedSharedPreferences even runs into this problem when it stores the encryption key in KeyStore. As long as the encryption key is on the device, the data is vulnerable. The only semi-solution is to put the key on a server, but that means the key must exist in the device at some point (either in storage or in memory).Manille
G
10

Using the snippet provided by Richard, you can encrypt the password before saving it. The preferences API however doesn't provide an easy way to intercept the value and encrypt it - you can block it being saved via an OnPreferenceChange listener, and you theoretically could modify it through a preferenceChangeListener, but that results in an endless loop.

I had earlier suggested adding a "hidden" preference in order to accomplish this. It's definitely not the best way. I'm going to present two other options that I consider to be more viable.

First, the simplest, is in a preferenceChangeListener, you can grab the entered value, encrypt it, and then save it to an alternative preferences file:

  public boolean onPreferenceChange(Preference preference, Object newValue) {
      // get our "secure" shared preferences file.
      SharedPreferences secure = context.getSharedPreferences(
         "SECURE",
         Context.MODE_PRIVATE
      );
      String encryptedText = null;
      // encrypt and set the preference.
      try {
         encryptedText = SimpleCrypto.encrypt(Preferences.SEED,(String)newValue);

         Editor editor = secure.getEditor();
         editor.putString("encryptedPassword",encryptedText);
         editor.commit();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      // always return false.
      return false; 
   }

The second way, and the way I now prefer, is to create your own custom preference, extending EditTextPreference, @Override'ing the setText() and getText() methods, so that setText() encrypts the password, and getText() returns null.

Glandule answered 2/3, 2011 at 14:56 Comment(2)
I know this is pretty old, but would you mind posting your code for your custom version of EditTextPreference, please?Berriman
Never mind, I found a usable sample here groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M and I've got it working now. Thanks for suggesting this approach.Berriman
J
6

Okay; it's been a while since the answer is kind-of mixed, but here's a few common answers. I researched this like crazy and it was hard to build a good answer

  1. The MODE_PRIVATE method is considered generally safe, if you assume that the user didn't root the device. Your data is stored in plain text in a part of the file system that can only be accessed by the original program. This makings grabbing the password with another app on a rooted device easy. Then again, do you want to support rooted devices?

  2. AES is still the best encryption you can do. Remember to look this up if you are starting a new implementation if it's been a while since I posted this. The largest issue with this is "What to do with the encryption key?"

So, now we are at the "What to do with the key?" portion. This is the hard part. Getting the key turns out to be not that bad. You can use a key derivation function to take some password and make it a pretty secure key. You do get into issues like "how many passes do you do with PKFDF2?", but that's another topic

  1. Ideally, you store the AES key off the device. You have to figure out a good way to retrieve the key from the server safely, reliably, and securely though

  2. You have a login sequence of some sort (even the original login sequence you do for remote access). You can do two runs of your key generator on the same password. How this works is that you derive the key twice with a new salt and a new secure initialization vector. You store one of those generated passwords on the device, and you use the second password as the AES key.

When you log in, you re-derive the key on the local login and compare it to the stored key. Once that is done, you use derive key #2 for AES.

  1. Using the "generally safe" approach, you encrypt the data using AES and store the key in MODE_PRIVATE. This is recommended by a recent-ish Android blog post. Not incredibly secure, but way better for some people over plain text

You can do a lot of variations of these. For example, instead of a full login sequence, you can do a quick PIN (derived). The quick PIN might not be as secure as a full login sequence, but it's many times more secure than plain text

Jegar answered 14/5, 2013 at 13:21 Comment(0)
C
5

I know this is a little bit of necromancy, but you should use the Android AccountManager. It's purpose-built for this scenario. It's a little bit cumbersome but one of the things it does is invalidate the local credentials if the SIM card changes, so if somebody swipes your phone and throws a new SIM in it, your credentials won't be compromised.

This also gives the user a quick and easy way to access (and potentially delete) the stored credentials for any account they have on the device, all from one place.

SampleSyncAdapter is an example that makes use of stored account credentials.

Cattegat answered 4/4, 2012 at 18:4 Comment(3)
Please note that using the AccountManager is not more secure than any other method provided above! developer.android.com/training/id-auth/…Dias
The use case for AccountManager is when the account has to be shared between different apps, and apps from different authors. Storing the password and giving it to any requesting app would not be appropriate. If the usage of the user/password is only for a single app, don't use AccountManager.Plashy
@dolmen, that's not quite correct. The AccountManager won't give the account password to any app whose UID doesn't match the Authenticator's. The name, yes; the auth token, yes; the password, no. If you try, it'll throw a SecurityException. And the use case is much broader than that. developer.android.com/training/id-auth/identify.htmlCattegat
M
5

I'll throw my hat into the ring just to talk about securing passwords in general on Android. On Android, the device binary should be considered compromised - this is the same for any end application which is in direct user control. Conceptually, a hacker could use the necessary access to the binary to decompile it and root out your encrypted passwords and etc.

As such there's two suggestions I'd like to throw out there if security is a major concern for you:

1) Don't store the actual password. Store a granted access token and use the access token and the signature of the phone to authenticate the session server-side. The benefit to this is that you can make the token have a limited duration, you're not compromising the original password and you have a good signature that you can use to correlate to traffic later (to for instance check for intrusion attempts and invalidate the token rendering it useless).

2) Utilize 2 factor authentication. This may be more annoying and intrusive but for some compliance situations unavoidable.

Melicent answered 30/10, 2014 at 17:0 Comment(0)
A
5

This is a supplemental answer for those arriving here based on the question title (like I did) and don't need to deal with the security issues related to saving passwords.

How to use Shared Preferences

User settings are generally saved locally in Android using SharedPreferences with a key-value pair. You use the String key to save or look up the associated value.

Write to Shared Preferences

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Use apply() instead of commit() to save in the background rather than immediately.

Read from Shared Preferences

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

The default value is used if the key isn't found.

Notes

  • Rather than using a local key String in multiple places like I did above, it would be better to use a constant in a single location. You could use something like this at the top of your settings activity:

      final static String PREF_MY_INT_KEY = "myInt";
    
  • I used an int in my example, but you can also use putString(), putBoolean(), getString(), getBoolean(), etc.

  • See the documentation for more details.

  • There are multiple ways to get SharedPreferences. See this answer for what to look out for.

Asquith answered 25/10, 2017 at 2:32 Comment(0)
G
2

You can also check out this little lib, containing the functionality you mention.

https://github.com/kovmarci86/android-secure-preferences

It is similar to some of the other aproaches here. Hope helps :)

Gromme answered 21/9, 2013 at 21:51 Comment(0)
B
1

This answer is based on a suggested approach by Mark. A custom version of the EditTextPreference class is created which converts back and forth between the plain text seen in the view and an encrypted version of the password stored in the preferences storage.

As has been pointed out by most who have answered on this thread, this is not a very secure technique, although the degree of security depends partly on the encryption/decryption code used. But it's fairly simple and convenient, and will thwart most casual snooping.

Here is the code for the custom EditTextPreference class:

package com.Merlinia.OutBack_Client;

import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.util.Base64;

import com.Merlinia.MEncryption_Main.MEncryptionUserPassword;


/**
 * This class extends the EditTextPreference view, providing encryption and decryption services for
 * OutBack user passwords. The passwords in the preferences store are first encrypted using the
 * MEncryption classes and then converted to string using Base64 since the preferences store can not
 * store byte arrays.
 *
 * This is largely copied from this article, except for the encryption/decryption parts:
 * https://groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M
 */
public class EditPasswordPreference  extends EditTextPreference {

    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context) {
        super(context);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet, int defaultStyle) {
        super(context, attributeSet, defaultStyle);
    }


    /**
     * Override the method that gets a preference from the preferences storage, for display by the
     * EditText view. This gets the base64 password, converts it to a byte array, and then decrypts
     * it so it can be displayed in plain text.
     * @return  OutBack user password in plain text
     */
    @Override
    public String getText() {
        String decryptedPassword;

        try {
            decryptedPassword = MEncryptionUserPassword.aesDecrypt(
                     Base64.decode(getSharedPreferences().getString(getKey(), ""), Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
            decryptedPassword = "";
        }

        return decryptedPassword;
    }


    /**
     * Override the method that gets a text string from the EditText view and stores the value in
     * the preferences storage. This encrypts the password into a byte array and then encodes that
     * in base64 format.
     * @param passwordText  OutBack user password in plain text
     */
    @Override
    public void setText(String passwordText) {
        byte[] encryptedPassword;

        try {
            encryptedPassword = MEncryptionUserPassword.aesEncrypt(passwordText);
        } catch (Exception e) {
            e.printStackTrace();
            encryptedPassword = new byte[0];
        }

        getSharedPreferences().edit().putString(getKey(),
                                          Base64.encodeToString(encryptedPassword, Base64.DEFAULT))
                .commit();
    }


    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        if (restoreValue)
            getEditText().setText(getText());
        else
            super.onSetInitialValue(restoreValue, defaultValue);
    }
}

This shows how it can be used - this is the "items" file that drives the preferences display. Note it contains three ordinary EditTextPreference views and one of the custom EditPasswordPreference views.

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <EditTextPreference
        android:key="@string/useraccountname_key"
        android:title="@string/useraccountname_title"
        android:summary="@string/useraccountname_summary"
        android:defaultValue="@string/useraccountname_default"
        />

    <com.Merlinia.OutBack_Client.EditPasswordPreference
        android:key="@string/useraccountpassword_key"
        android:title="@string/useraccountpassword_title"
        android:summary="@string/useraccountpassword_summary"
        android:defaultValue="@string/useraccountpassword_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverip_key"
        android:title="@string/outbackserverip_title"
        android:summary="@string/outbackserverip_summary"
        android:defaultValue="@string/outbackserverip_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverport_key"
        android:title="@string/outbackserverport_title"
        android:summary="@string/outbackserverport_summary"
        android:defaultValue="@string/outbackserverport_default"
        />

</PreferenceScreen>

As for the actual encryption/decryption, that is left as an exercise for the reader. I'm currently using some code based on this article http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/, although with different values for the key and the initialization vector.

Berriman answered 6/9, 2013 at 5:49 Comment(0)
I
1

First of all I think User's data shouldn't be stored on phone, and if it is must to store data somewhere on the phone it should be encrypted with in the apps private data. Security of users credentials should be the priority of the application.

The sensitive data should be stored securely or not at all. In the event of a lost device or malware infection, data stored insecurely can be compromised.

Iormina answered 3/12, 2013 at 10:40 Comment(0)
G
1

I use the Android KeyStore to encrypt the password using RSA in ECB mode and then save it in the SharedPreferences.

When I want the password back I read the encrypted one from the SharedPreferences and decrypt it using the KeyStore.

With this method you generate a public/private Key-pair where the private one is safely stored and managed by Android.

Here is a link on how to do this: Android KeyStore Tutorial

Gorrono answered 7/11, 2018 at 13:23 Comment(0)
R
1

As others already pointed out you can use SharedPreferences generally but if you would like to store data encrypted it's a bit inconvenient. Fortunately, there is an easier and quicker way to encrypt data now since there is an implementation of SharedPreferences that encrypts keys and values. You can use EncryptedSharedPreferences in Android JetPack Security.

Just add AndroidX Security into your build.gradle:

implementation 'androidx.security:security-crypto:1.0.0-rc01'

And you can use it like this:

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
    "secret_shared_prefs",
    masterKeyAlias,
    context,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();

See more details: https://android-developers.googleblog.com/2020/02/data-encryption-on-android-with-jetpack.html

Official docs: https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

Raddatz answered 18/2, 2021 at 5:30 Comment(0)
C
0

This is how I am doing it.

This does not give errors in strict mode.

public class UserPreferenceUtil
{

    private static final String  THEME = "THEME";
    private static final String  LANGUAGE = "LANGUAGE";

    public static String getLanguagePreference(Context context)
    {

        String lang = getPreferenceByKey(context,LANGUAGE);

        if( lang==null || "System".equalsIgnoreCase(lang))
        {
            return null;
        }


        return lang;
    }

    public static void saveLanguagePreference(Context context,String value)
    {
        savePreferenceKeyValue(context, LANGUAGE,value);
    }

    public static String getThemePreference(Context context)
    {

        return getPreferenceByKey(context,THEME);
    }

    public static void saveThemePreference(Context context, String value)
    {
        savePreferenceKeyValue(context,THEME,value);

    }

    public static String getPreferenceByKey(Context context, String preferenceKey )
    {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

        String value = sharedPreferences.getString(preferenceKey, null);

        return value;
    }

    private static void savePreferenceKeyValue(Context context, String preferenceKey, String value)
    {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(preferenceKey,value);
        editor.apply();

    }


}

My app does not need a password. However, rather than saving passwords or encrypted passwords, I would save a one-way hash. When the user logs in, I will hash the input the same way and match it with the stored hash.

Carbrey answered 28/5, 2022 at 15:37 Comment(0)
B
-3

you need to use the sqlite, security apit to store the passwords. here is best example, which stores passwords, -- passwordsafe. here is link for the source and explanation -- http://code.google.com/p/android-passwordsafe/

Bowden answered 27/4, 2009 at 6:9 Comment(3)
The OP needs to store one username and password pair. It would be ridiculous to consider creating an entire database table for this one useAstrogate
@Astrogate i respectfully disagree - i can see at least 1 other use of a user/passwords sqlite table. IF YOU CONSIDER THE RISK (of using sqlite) ACCEPTABLE, besides simple application login authentication, you could use the table to store multiple ftp passwords (if your app uses ftp - mine do sometimes), for example. plus, creating a sqlite adapter class for this manipulation is boilerplate simple.Thriftless
Nice resurrection of a two-year-old comment! To be fair, my comment was a year after the answer :) Even with a handful of FTP passwords, the overhead is much larger with an SQLite table than with SharedPreferences both in terms of space and coding. Surely that can't be necessaryAstrogate
H
-3

shared preferences is easiest way to store our application data. but it is possible that anyone can clear our shared preferences data through application manager.so i don't think it is completely safe for our application.

Hercule answered 18/10, 2013 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.