3

I am trying to utilize Android's Account Manager to store user's app credentials. Although I am not saving the user's password, I wanted to save other security keys to the account's UserData. According to the documentation quoted below, this should NOT be accessible by applications with different UID.

public String getUserData (Account account, String key)

Gets the user data named by "key" associated with the account. This is intended for authenticators and related code to store arbitrary metadata along with accounts. The meaning of the keys and values is up to the authenticator for the account.

It is safe to call this method from the main thread.

This method requires the caller to hold the permission AUTHENTICATE_ACCOUNTS and to have the same UID as the account's authenticator.

Parameters account - The account to query for user data Returns The user data, null if the account or key doesn't exist

To test this, I created an application that creates an account and saves some contents to UserData. I also created another application that accesses the accounts of the first app. Below are the snippets:

First app:

AccountManager am = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
final Account account = new Account("Account Name", "my.account.type");
am.addAccountExplicitly(account, null, null);
am.setAuthToken(account, "my.account.type", "some auth token");
am.setUserData(account, "key.for.secure.user.data", "some secure data");

Second app:

AccountManager am =  (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE);
Account[] accountsFromFirstApp = am.getAccountsByType("my.account.type");
for(Account acct: accountsFromFirstApp){
    printToLogs(acct.name);
    printToLogs(am.getUserData(acct, "key.for.secure.user.data"));
}

Based on the documentation above, I expected the second app's getUserData() to return an exception for not having the same UID as the owner app. Surprisingly, I was able to access the user data of the first app with no errors.

But when I tried to access accounts from google by using "com.google" as the accountType, I got the expected exception.

What was wrong with my implementation? Did I miss some configuration that was not stated in Android's documentation? Any help will be very much appreciated.

On the second thought, If these user data can just be accessed that easily (assuming that the other applications know my app's account type), then what's the difference of storing strings in UserData from storing them in SharedPreferences instead?

Dynameyes
  • 435
  • 5
  • 15
  • which android version are you using? have you compiled both apps in releasemode with different signing certificates? does your app manifest contain a [manifest android:sharedUserId](http://developer.android.com/guide/topics/manifest/manifest-element.html#uid)?Do both apps have different [manifest packagename](http://developer.android.com/guide/topics/manifest/manifest-element.html#package)? – k3b Mar 19 '15 at 07:33
  • By the time of testing, I used several android versions such as KitKat and variations of JellyBean. I haven't tried compiling them in release modes yet. I did not specify android:sharedUserId and I'm sure they have different UID as I also put them in logs. They also have different package names. – Dynameyes Mar 19 '15 at 14:27
  • Now that I'm re-reading the replies, to answer k3b's question if I used different signing certificates, I did not. It's actually the same debug certificate that I used to test this. But the documentation does not say anything about this. Should it really matter? – Dynameyes Mar 19 '15 at 15:23

2 Answers2

0

From the AccountManager documentation:

This class provides access to a centralized registry of the user's online accounts. The user enters credentials (username and password) once per account, granting applications access to online resources with "one-click" approval.

Accounts managed by the AccountManager are centralized and reusable, e.g. all Google apps can use the same account, and not every app has to create its own Google account.

So one of the ideas of the AccountManager, as far as i understand, is to have reusable accounts, accessible from different apps.

But as the stored credentials are accessible from different places, you shouldn't store any plain text passwords in the AccountManager.

May this topic is interesting for you: What should I use AccountManager for?

Community
  • 1
  • 1
Thomas S.E.
  • 1,528
  • 16
  • 28
  • Thanks for your response! I suppose that answer was for my last question about the advantage of using AccountManager over SharedPreferences. Although I understand the idea you're stating, my primary concern is that the account manager method was not returning an exception contrary to what was stated in AccountManager.documentation. Any ideas? – Dynameyes Mar 19 '15 at 14:32
  • 7
    Maybe its because you signed them both with your debug keystore? I have currently no other idea ... – Thomas S.E. Mar 20 '15 at 07:50
  • 1
    I tested with sames and differents keystore apps. So I confirm Thomas' answer is right. With differents keystores you dont get exception, but getAccountsByType() from AccountManger return an empty array. – smora Jan 02 '16 at 17:03
0

If your data is Personal data, you can encrypt it, so no other apps can read them.

I.E using AES Encryption like this:

public class AESCryptor
{
    private static final String ALGORITHM = "AES";
    // 16-bit Key for encryption (Change to yours)
    private static final String KEY = "XXXXXXXXXXXXXXX";

    public static String encrypt(String value) throws Exception
    {
        Key key = generateKey();
        @SuppressLint("GetInstance") Cipher cipher = Cipher.getInstance(AESCryptor.ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte [] encryptedByteValue = cipher.doFinal(value.getBytes("utf-8"));
        return Base64.encodeToString(encryptedByteValue, Base64.DEFAULT);

    }

    public static String decrypt(String value) throws Exception
    {
        Key key = generateKey();
        @SuppressLint("GetInstance") Cipher cipher = Cipher.getInstance(AESCryptor.ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decryptedValue64 = Base64.decode(value, Base64.DEFAULT);
        byte [] decryptedByteValue = cipher.doFinal(decryptedValue64);
        return new String(decryptedByteValue,"utf-8");

    }

    private static Key generateKey() {
        return new SecretKeySpec(AESCryptor.KEY.getBytes(),AESCryptor.ALGORITHM);
    }
}

I use it in my apps, and it works!

This maybe doesn't answer the question

User data in account manager is accessible by other applications

But its answered by @thomas-s-e

Regards, @developerfromjokela

DFJ
  • 93
  • 2
  • 9