AndroidKeystore密钥无法初始化密码

时间:2018-06-27 14:21:14

标签: android keystore android-keystore

我在我的应用程序中使用AndroidKeyStore来存储密钥,并且正在使用KeyGenParameterSpec类生成该密钥。这是代码:

final SecretKey secretKey;
        final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
        final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setUserAuthenticationRequired(true)
            .setUserAuthenticationValidityDurationSeconds(<validityInSeconds>)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            .setRandomizedEncryptionRequired(false)
            .build();
        keyGenerator.init(keyGenParameterSpec);
        secretKey = keyGenerator.generateKey();

然后,我尝试使用此密钥初始化密码:

final Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/"
                + KeyProperties.ENCRYPTION_PADDING_PKCS7);

            cipher.init(Cipher.ENCRYPT_MODE, secretKey);

现在,AndroidKeyStore似乎有一个奇怪的问题。 cipher.init在某些情况下会失败,而且很奇怪的一点是,除了为validityInSecs尝试不同的值外,我将所有内容保持不变。如果失败,它将失败并显示以下错误:

Caused by: java.lang.NullPointerException: Attempt to get length of null array
        at org.spongycastle.crypto.params.KeyParameter.<init>(KeyParameter.java:13)
        at org.spongycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:615)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2659)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2570)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
        at javax.crypto.Cipher.init(Cipher.java:973)
        at javax.crypto.Cipher.init(Cipher.java:908)

我尝试为validityInSec使用不同的值,但从某些值失败(对于非常低的值),而有的则成功(当我增加值时)。以下是一些数据点:

  

10s->失败
1800s->成功
100s->失败
750s->   成功

另一个有趣的事情是,对于相同的值,它有时会失败,有时会成功。这些值的示例是750s625s

我不知道这是怎么回事。

  

设备:一加3吨
  操作系统:8.0.0

请帮助。

编辑:

这里的堆栈跟踪有些误导,因为它具有Spongycastle库中的跟踪。

那是android框架类中的错误。对于密码,它将一一尝试与所有不同的提供程序一起使用,如果失败,它将异常存储在单个变量中。问题是,它不会更新该异常变量。因此,如果所有提供商的密码均失败,则它抛出的异常来自第一个提供商。因此,它为您提供了仅尝试了该提供程序的表达式。

以下是相关代码: http://androidxref.com/8.0.0_r4/xref/libcore/ojluni/src/main/java/javax/crypto/Cipher.java#2546

就我而言,在成功案例中逐级调试之后,我发现它正在尝试除SC以外的其他提供程序。相关屏幕截图:

enter image description here

更新2:

我尝试了final Cipher cipher = Cipher.getInstance(<transformation>,"AndroidKeyStoreBCWorkaround");

现在,当我通过duration作为1800s时,它成功了。当我通过40s时,它失败并显示以下错误:

06-28 20:06:55.462 624-624/in.zeta.android E/EncryptedStoreService: android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:741)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:777)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2663)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2556)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
        at javax.crypto.Cipher.init(Cipher.java:830)
        at javax.crypto.Cipher.init(Cipher.java:771)
  • 在尝试初始化密码之前,我将尝试对用户进行身份验证
  • 尽管我确实想解决这个问题,但在我的生产环境中,我的时间总之为1800s。可以忘记在较短的持续时间内失败吗?

2 个答案:

答案 0 :(得分:1)

我不知道validityInSeconds会如何影响代码,但此异常与之无关

Caused by: java.lang.NullPointerException: Attempt to get length of null array
    at org.spongycastle.crypto.params.KeyParameter.<init>(KeyParameter.java:13)

由于密钥是由 AndroidKeyStore 管理的,但是Cipher使用的是加密提供程序 spongycastle ,导致了该异常。 AES密钥不可提取,海绵宝宝需要它才能加密

在初始化Cipher时,从项目中删除海绵堡或强制使用AndroidKeyStore

 Cipher cipher = Cipher.getInstance(algorithm, "AndroidKeyStore");

按照在操作系统中的安装顺序选择加密提供程序。如果未明确指出加密提供者,则Android将根据上述选择提供spongycastle或AndroidKeyStore。我不知道validityInSeconds的影响如何,但是可能会有一些内部Android行为会改变顺序

答案 1 :(得分:1)

我尝试了

final Cipher cipher = Cipher.getInstance(<transformation>,"AndroidKeyStoreBCWorkaround");

通过放置断点,我发现这是真正起作用的提供程序。

现在,当我以duration的身份通过1800s时,它成功了。当我通过40s时,它失败并显示以下错误:

06-28 20:06:55.462 624-624/in.zeta.android E/EncryptedStoreService: android.security.keystore.UserNotAuthenticatedException: User not authenticated
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:741)
        at android.security.KeyStore.getInvalidKeyException(KeyStore.java:777)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:54)
        at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:109)
        at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2663)
        at javax.crypto.Cipher.tryCombinations(Cipher.java:2556)
        at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
        at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
        at javax.crypto.Cipher.init(Cipher.java:830)
        at javax.crypto.Cipher.init(Cipher.java:771)

堆栈跟踪中的相关源代码链接:

https://android.googlesource.com/platform/frameworks/base/+/508e665/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java#215

https://android.googlesource.com/platform/frameworks/base/+/83a86c5/keystore/java/android/security/KeyStoreCryptoOperationUtils.java#42

https://android.googlesource.com/platform/frameworks/base/+/master/keystore/java/android/security/KeyStore.java#707

在第二个链接之后,它不会进入KeyStore.OP_AUTH_NEEDED,否则它将从那里返回null。因此,大多数情况下,它处于LOCKED状态。 (https://android.googlesource.com/platform/frameworks/base/+/master/keystore/java/android/security/KeyStore.java#58

我知道在初始化密码时是否需要用户认证,但是很好奇为什么密码在1800s期间成功。我为> 1800s保留了我的应用程序的打开状态,然后尝试在此期间进行尝试,但由于相同的异常而失败。然后我通过了18000s并成功了。因此,事实证明,检查用户是否在过去的keyvalidity秒内通过了身份验证是很明显的,如果我没记错的话,它也存在于文档中。

因此,由于 Cipher类抛出了错误的异常>,整个混乱就在那里了。<< / p>

在尝试初始化密码之前,我将尝试对用户进行身份验证