Background
On one of the apps I work on, I store important stuff (tokens) into EncryptedSharedPreferences (taken from here and here):
/** a hardware-encrypted based shared preference (for the values).
* Note that it is a bit slow, so it's better to always use it in a background thread.
* Also, avoid having it being backed-up in the manifest, as it's hardware based and will become useless: https://stackoverflow.com/a/63795282/878126*/
object SecuredSharedPreferences {
private var cachedDefaultSharedPreferences: SharedPreferences? = null
/**warning: using this function can take some time (249 ms on Pixel 4, for example). Very recommended to avoid calling it on UI thread */
@WorkerThread
fun getDefaultSecuredSharedPreferences(context: Context): SharedPreferences {
if (cachedDefaultSharedPreferences != null)
return cachedDefaultSharedPreferences!!
synchronized(this) {
if (cachedDefaultSharedPreferences != null)
return cachedDefaultSharedPreferences!!
cachedDefaultSharedPreferences = getSecuredSharedPreferences(context, context.packageName + "_secured_preferences")
}
return cachedDefaultSharedPreferences!!
}
@WorkerThread
private fun getSecuredSharedPreferences(context: Context, fileName: String): SharedPreferences {
val masterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
return EncryptedSharedPreferences.create(context, fileName, masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
}
gradle:
implementation 'androidx.security:security-crypto:1.1.0-alpha03'
The problem
I've noticed 2 bugs being reported via Crashlytics when using this code (reported here):
- First one is of the
MasterKey.Builder
line ofGeneralSecurityException
:
Fatal Exception: java.security.GeneralSecurityException: Keystore operation failed
at androidx.security.crypto.MasterKeys.generateKey(MasterKeys.java:146)
at androidx.security.crypto.MasterKeys.getOrCreate(MasterKeys.java:97)
at androidx.security.crypto.MasterKey$Builder.buildOnM(MasterKey.java:357)
at androidx.security.crypto.MasterKey$Builder.build(MasterKey.java:314)
...
Caused by java.security.ProviderException: Keystore operation failed
at android.security.keystore.AndroidKeyStoreKeyGeneratorSpi.engineGenerateKey(AndroidKeyStoreKeyGeneratorSpi.java:372)
at javax.crypto.KeyGenerator.generateKey(KeyGenerator.java:612)
at androidx.security.crypto.MasterKeys.generateKey(MasterKeys.java:142)
at androidx.security.crypto.MasterKeys.getOrCreate(MasterKeys.java:97)
at androidx.security.crypto.MasterKey$Builder.buildOnM(MasterKey.java:357)
at androidx.security.crypto.MasterKey$Builder.build(MasterKey.java:314)
- Second one is on
EncryptedSharedPreferences.create
line ofKeyStoreException
, and occurs more often and for more users :
Fatal Exception: java.security.KeyStoreException: the master key android-keystore://_androidx_security_master_key_ exists but is unusable
at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewMasterKey(AndroidKeysetManager.java:275)
at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:236)
at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:155)
at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:120)
...
Caused by java.security.UnrecoverableKeyException: Failed to obtain information about key
at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(AndroidKeyStoreProvider.java:282)
at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:98)
at java.security.KeyStore.getKey(KeyStore.java:825)
at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.<init>(AndroidKeystoreAesGcm.java:58)
at com.google.crypto.tink.integration.android.AndroidKeystoreKmsClient.getAead(AndroidKeystoreKmsClient.java:164)
at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewMasterKey(AndroidKeysetManager.java:267)
at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:236)
at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:155)
at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:120)
What I've tried
Searching the Internet, I've found clues only for the first exception (GeneralSecurityException), that it might be that it occurs for custom ROMs, as they might not implement well the hardware keys for encryption.
And indeed, looking at the devices on Crashlytics, and looking at the Android version of each, I've found that they are ahead of what I see about the latest version that was supported for them.
For the second exception, sadly, I couldn't find any explanation and no solution either. I think it might be related to recovery of apps, but it's weird as it occurs quite often. On reddit (here), someone wrote that in case of such an exception, he chose to wrap the initialization of EncryptedSharedPreferences with "clear all data if fails" and bite the bullet
. Also suggested it might be related to having android:allowBackup
being disabled (and indeed it is).
The question
Why do these exceptions occur? What can I do against them?
Is clear-data the only thing that can be done? I'm not even sure it really helps, because if I choose to have it, it means the crash report will be gone each time it's about to happen...
Is it related to android:allowBackup
being disabled?
from Getting KeyStoreException and GeneralSecurityException by using EncryptedSharedPreferences, how can I solve those?
No comments:
Post a Comment