三星手机指纹解锁后出现错误:android.security.KeyStoreException: Key user not authenticated。

21
我的应用程序使用Android 6.0指纹API来保护Android KeyStore中的AES密钥。存储的密钥只有在用户通过指纹传感器进行身份验证时才能使用,因为KeyGenParameterSpec被初始化为setUserAuthenticationRequired(true)。
当用户触摸传感器时,我从回调onAuthenticationSucceeded(Cipher)中获取已初始化的Cipher,并将其用于解密。
这在除三星手机外的其他设备上都能正常工作。而在三星手机上,当我尝试使用返回的Cipher时,有时会抛出android.security.KeyStoreException: Key user not authenticated的异常。这意味着即使Cipher是由onAuthenticationSucceeded(Cipher)返回的,Android KeyStore仍认为用户未通过指纹传感器进行身份验证。
似乎这个崩溃主要发生在应用程序长时间未使用后。当应用程序启动后,通常就能正常工作。
由于这个错误只在三星手机等少数设备上随机出现,看起来是由于三星对Android 6.0 KeyStore和FingerPrint API实现的内部时序问题所导致。
编辑:也在一加和宏碁手机上遇到了此问题。

1
当用户在设备设置中添加新指纹时,会发生这种情况吗? - Eugen Martynov
9个回答

21

设置KeyGenParameterSpec.setUserAuthenticationRequired(false)可能会导致潜在的安全问题。应该类似于KeyPermanentlyInvalidatedException来处理上述错误。如果在创建SecretKey之后添加了新指纹,将在Cipher初始化时抛出KeyPermanentlyInvalidatedException。但是,如果Cipher在添加新指纹之前初始化,则在使用该Cipher进行加密或解密时会出现上述针对未经身份验证的用户的KeyStoreException。

很容易重现这个错误。当您的应用程序的指纹验证屏幕在后台时,请尝试添加一个新指纹。现在切换回应用程序,并输入指纹,加密或解密方法将抛出此错误。我可以通过捕获异常并将其与KeyPermanentlyInvalidatedException相同地处理来解决此问题。


这个错误在三星手机(以及其他一些手机)上随机发生,而在像LG这样的其他手机上从未发生过。如果用户必须为每第5或第10次访问应用程序重新验证以刷新存储的密码,则对用户来说并不舒适。 - petrsyn
是的,我也只在三星设备上看到过这个问题。但人们有多经常向他们的设备添加新指纹呢?三星最多支持4个指纹。所以除非有人经常玩弄他们的指纹设置,否则情况不会太糟糕。你是否在其他情况下看到过这个错误,除了添加新指纹之外? - NullPointer
21
很遗憾,这个 bug 与添加新指纹无关,它是随机发生的。 - petrsyn
请问您能否解释一下如何处理KeyPermanentlyInvalidatedException,或者提供一个已经处理过该异常的链接。谢谢。 - Smit Davda
我一直将KeyPermanentlyInvalidatedException视为其他致命错误一样处理,通过回退到使用密码进行身份验证。一旦用户经过身份验证,就会生成一个新的SecretKey,并使用使用新的SecretKey的Cipher加密数据。我很好奇其他人是如何处理这个问题的,看看是否有更好的方法来解决它。 - NullPointer

6
请不要听从"setUserAuthenticationRequired(false)"的建议。
我已经在三星手机上复现了这个问题,我的理解是你监听了两次,通过一次调用进行身份验证,但是通过另一次引用。请添加日志并检查您只启动一次指纹监听。

请注意,这种情况偶尔会随机发生。并非所有三星手机都会出现此问题。 - petrsyn
2
这肯定是因为同时监听的次数太多而导致的竞争条件。是的,它只会在某些三星手机上发生。 - Chris Merrick
我同意存在某种竞态条件。虽然我的代码实际上没有重复监听,但我有一个方法,在尝试完成登录过程时,我会先调用initSign()来查看签名是否已失效,然后稍后再次调用initSign()。 - Montwell

4

我也遇到过这个问题。在我的情况下,是因为我不小心通过两次调用FingerprintManager.authenticate()同时开启了两个指纹识别。一旦我删除了第二个调用,该错误就消失了。


是的,没错。由于我无法弄清楚为什么它被设置了两次,所以我将其保留并部署了。有趣的是,三星S8会崩溃,Pixel2在这个错误上表现得很奇怪。确保你实际上正确地取消了身份验证非常重要,在我的情况下,我忘记将CancellationSignal对象传递给另一个类,因此只剩下了聋哑的CancellationSignal。 - ochitos

3

更新: 这是与Android 8.0相关的已知问题 https://issuetracker.google.com/issues/65578763

我刚刚在三星手机上看到了这个错误,似乎是在添加新指纹时引起的。在我们的代码中,我们希望在signature.initSign()期间抛出KeyPermanentlyInvalidatedException异常。但是这并没有发生,初始化的签名成功传递给FingerprintManager作为CryptoObject的一部分。然后,指纹验证成功并调用onAuthenticationSucceeded。当尝试调用signature.update(byte[] bytes)时发生错误。

我认为期望的行为应该是抛出KeyInvalidatedException异常,但我不确定我们是否可以期望这个问题得到解决。我的解决方案是在onAuthenticationSucceeded侧捕获它。

@Override
    public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
        Log.d(LOG_TAG, "Device Authentication Succeeded");
        try {
            Signature signature = result.getCryptoObject().getSignature();
            String authData = getAuthData();
            signature.update(authData.getBytes());
            // do something with signature

        } catch (SignatureException e) {
            Log.d(LOG_TAG, e.getMessage());
            if(e.getMessage() != null && e.getMessage().contains("Key user not authenticated")) {
                // handle as if were KeyPermanentlyInvalidatedException
            } else {
                Log.d(LOG_TAG, e.getMessage());
                // handle as regular error
            }
        }
    }

3

这似乎是Android更新后出现的一个错误。

我在一加手机上遇到过这个问题。我从设置中删除了设备锁,并重新设置了一次。之后,问题就解决了。


2

当我使用Android 8上的三星Galaxy S8时,也遇到了这个问题。为了解决这个问题,我尝试从密钥库中删除密钥,然后生成一个新密钥,然后使用新生成的密钥来加密和解密数据。

try {
    keyStore.deleteEntry(KEY_ALIAS);
} catch (KeyStoreException e) {
    e.printStackTrace();
}

2

由于我不希望提到的制造商很快解决这个问题,所以我通过为三星、一加、华硕和其他一些设备设置 KeyGenParameterSpec.setUserAuthenticationRequired(false) 来解决了这个问题。


24
这个答案暗示着人们应该根据手机是否为三星手机来设置用户认证要求,这在很多方面都是错误的。setUserAuthenticationRequired是与安全计划有关的问题,而不是修复漏洞的问题。 - Matt Quigley
很遗憾,我自己也不知道答案,但我怀疑这并不是三星手机的问题,因为在许多设备上都会出现此类情况。我们所知道的是,当遇到这个错误时,我的团队发现他们没有考虑到有些情况下设备需要身份验证,比如如果过了24小时,就必须输入PIN码。 - Matt Quigley
事实上,如果这是问题的话,那么需要回答的问题可能是:当你只处理指纹时,如何获得用户身份验证? - Matt Quigley
5
有趣的事,原来这是三星6.0.1设备的问题,我们最终只需执行“setUserAuthenticationRequired(false)”即可解决。我们甚至还请外部安全公司进行了审计,除非手机已被root,否则实际上并不存在安全问题。 - Matt Quigley
1
@Wirling,如果出现异常,您会如何处理并仍然允许事情继续进行?我的意思是,我是否仍将获得已初始化的Cipher? - John Ernest Guadalupe
显示剩余5条评论

1
当我使用RSA时,也遇到了这个问题,并且通过在加密数据之前创建公钥的副本来解决了它。
// create a copy of the public key -> workaround for android.security.KeyStoreException: Key user not authenticated
val publicKey = KeyFactory
    .getInstance("RSA")
    .generatePublic(X509EncodedKeySpec(keyPair.public.encoded))

// encrypt with the public key
val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val encryptedData = cipher.doFinal(data)

1

通过明确设置认证密钥的有效期,它可以在我的三星Galaxy S8上运行:

setUserAuthenticationValidityDurationSeconds(10);

然而,这使得在该时间段内多次使用密钥而无需进一步用户身份验证在技术上成为可能。

个人认为这并不是一个很大的风险。

我尚未使用这些保护措施测试过加密大流(可能需要几秒钟才能完成),我想知道如果加密任务超过有效期允许的时间会发生什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接