Search code examples
javaandroidsecuritykeystore

'Key user not authenticated', Understanding the Android KeyStore


I'm trying to store a secret securely an Android application. To do so, I want to encrypt them by using a key from the Android KeyStore similar to what you would do with the KeyChain on iOS. I followed this Guide and only have to consider Android 6.0+

From my understanding, the KeyStore is a secure storage provided by Android. However, I do not really understand who unlocks access to the keystore. What I currently do is, I create a key using:

private SecretKey generateKey(String keyAlias) {
    try {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);

        keyGenerator.init(new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setUserAuthenticationRequired(true)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .build());
        return keyGenerator.generateKey();
    } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
        // handling...
    }
}

Now using this key fails during encryption with an android.security.KeyStoreException: Key user not authenticated. I see that the setting setUserAuthenticationRequired(true) requires authentication, however, I don't see how this authentication is provided. I assumed the KeyStore would be accessible whenever the user has unlocked his phone. I read about the fingerprint sensor being required to authenticate the user for the KeyStore, however, many Android devices do not have a fingerprint sensor yet.


Solution

  • If you specify "AndroidKeyStore" as the provider, then a secure hardware-backed keystore will be used if the device supports it. What that means in practice is that it should be impossible* to extract private key material, even for your own app.

    It should also be impossible / very difficult for other apps to use keys that you have created. But I'm not really familiar with how that is enforced.

    You can check whether a key you created was generated by / is stored in secure hardware, by using the getOrigin and isInsideSecureHardware methods of KeyInfo.

    *Obviously this depends on the device's secure hardware module being well-implemented and free of exploitable bugs. There have been practical exploits found in the past, though they required physical access to the device IIRC. The point is that nothing is 100% secure.


    Now using this key fails during encryption with an android.security.KeyStoreException: Key user not authenticated.

    By default, setUserAuthenticationRequired(true) means that authorization is required for every single use of the key, and the only means of such authorization is through fingerprint authentication.

    You can use setUserAuthenticationValidityDurationSeconds to specify that you want the key to be usable for a certain amount of time after the last authorization. In this case, the key becomes usable as soon as the user provides any of their screen unlock credentials, whatever those are.