我在我的应用程序中使用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-> 成功
另一个有趣的事情是,对于相同的值,它有时会失败,有时会成功。这些值的示例是750s
和625s
。
我不知道这是怎么回事。
设备:一加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以外的其他提供程序。相关屏幕截图:
更新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
。可以忘记在较短的持续时间内失败吗?答案 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)
堆栈跟踪中的相关源代码链接:
在第二个链接之后,它不会进入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>
在尝试初始化密码之前,我将尝试对用户进行身份验证