16

背景

在我使用的一个应用程序中,我将重要的东西(令牌)存储到 EncryptedSharedPreferences 中(取自此处此处):

/** 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
        )
    }
}

毕业典礼:

implementation 'androidx.security:security-crypto:1.1.0-alpha03'

问题

我注意到使用此代码时通过 Crashlytics 报告了 2 个错误(在此处报告):

  1. 第一个是MasterKey.BuilderGeneralSecurityException
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)
  1. 第二个是EncryptedSharedPreferences.create在线的KeyStoreException,并且发生的频率更高,用户也更多:
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)

编辑:似乎还有更多类型的例外:

  1. 无效协议缓冲区异常:
Fatal Exception: com.google.crypto.tink.shaded.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
       at com.google.crypto.tink.shaded.protobuf.GeneratedMessageLite.parsePartialFrom(GeneratedMessageLite.java:1566)
       at com.google.crypto.tink.shaded.protobuf.GeneratedMessageLite.parseFrom(GeneratedMessageLite.java:1663)
       at com.google.crypto.tink.proto.Keyset.parseFrom(Keyset.java:957)
       at com.google.crypto.tink.integration.android.SharedPrefKeysetReader.read(SharedPrefKeysetReader.java:84)
       at com.google.crypto.tink.CleartextKeysetHandle.read(CleartextKeysetHandle.java:58)
       at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.read(AndroidKeysetManager.java:328)
       at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readOrGenerateNewKeyset(AndroidKeysetManager.java:287)
       at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:238)
       at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:160)
       at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:120)
  1. 空指针异常:
Fatal Exception: java.lang.NullPointerException: Attempt to invoke interface method 'android.security.keymaster.OperationResult android.security.IKeystoreService.begin(android.os.IBinder, java.lang.String, int, boolean, android.security.keymaster.KeymasterArguments, byte[], int)' on a null object reference
       at android.security.KeyStore.begin(KeyStore.java:501)
       at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:248)
       at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
       at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2977)
       at javax.crypto.Cipher.tryCombinations(Cipher.java:2884)
       at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2789)
       at javax.crypto.Cipher.chooseProvider(Cipher.java:956)
       at javax.crypto.Cipher.init(Cipher.java:1199)
       at javax.crypto.Cipher.init(Cipher.java:1143)
       at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.encryptInternal(AndroidKeystoreAesGcm.java:84)
       at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.encrypt(AndroidKeystoreAesGcm.java:72)
       at com.google.crypto.tink.integration.android.AndroidKeystoreKmsClient.validateAead(AndroidKeystoreKmsClient.java:248)
       at com.google.crypto.tink.integration.android.AndroidKeystoreKmsClient.getAead(AndroidKeystoreKmsClient.java:165)
       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)

我试过的

在 Internet 上搜索,我只发现了第一个异常 (GeneralSecurityException) 的线索,这可能是自定义 ROM 发生的,因为它们可能无法很好地实现用于加密的硬件密钥。

事实上,查看 Crashlytics 上的设备,查看每个设备的 Android 版本,我发现它们领先于我所看到的支持它们的最新版本。

对于第二个例外,可悲的是,我找不到任何解释,也没有解决方案。我认为这可能与应用程序的恢复有关,但这很奇怪,因为它经常发生。在 reddit(此处)上,有人写道,如果出现这种异常,他选择了wrap the initialization of EncryptedSharedPreferences with "clear all data if fails" and bite the bullet. 还建议它可能与android:allowBackup被禁用有关(确实如此)。

不知道其余的。

问题

为什么会出现这些异常?我能对他们做什么?

清除数据是唯一可以做的事情吗?我什至不确定它是否真的有帮助,因为如果我选择拥有它,这意味着每次它即将发生时崩溃报告都会消失......

android:allowBackup残疾有关系吗?

4

1 回答 1

3

我认为你android:allowBackup=false成为罪魁祸首的直觉是正确的。我遇到了类似的问题(当 allowBackup 设置为 false 时,加密的共享首选项在应用更新时引发错误)。在我的情况下,我设法通过安装、登录(设置加密首选项)然后更新到更新版本的应用程序并尝试读取首选项来重现。我最终通过删除解决了这个问题android:allowBackup=false

于 2021-01-05T05:26:32.543 回答