Android 指纹管理器期间的UserNotAuthenticatedException.authenticate()异常

Android 指纹管理器期间的UserNotAuthenticatedException.authenticate()异常,android,android-fingerprint-api,Android,Android Fingerprint Api,我有一个加密密码存储在Android密钥库中 我想通过使用指纹API对用户进行身份验证来解密该密码 据我所知,我必须调用fingertmanager.authenticate(CryptoObject CryptoObject)方法来开始侦听指纹结果。CryptoObject参数的创建方式如下: public static Cipher getDecryptionCipher(Context context) throws KeyStoreException { try {

我有一个加密密码存储在Android密钥库中

我想通过使用指纹API对用户进行身份验证来解密该密码

据我所知,我必须调用
fingertmanager.authenticate(CryptoObject CryptoObject)
方法来开始侦听指纹结果。CryptoObject参数的创建方式如下:

public static Cipher getDecryptionCipher(Context context) throws KeyStoreException {
    try {
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        SecretKey secretKey = getKeyFromKeyStore();
        final IvParameterSpec ivParameterSpec = getIvParameterSpec(context);

        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        return cipher;

    } catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | UnrecoverableKeyException | CertificateException | InvalidAlgorithmParameterException | InvalidKeyException e) {
        e.printStackTrace();

    }

    return null;
}

Cipher cipher = FingerprintCryptoHelper.getDecryptionCipher(getContext());
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
fingerprintManager.authenticate(cryptoObject, ...);
方法
getDecryptionCipher()
在调用
cipher.init()
之前工作正常。在这个调用中,我得到了一个
UserNotAuthenticatedException
,因为用户没有为此secretKey进行身份验证。这是有道理的。但这不是一个循环,不可能实现:

  • 要验证用户身份,我想使用他/她的指纹
  • 要监听他/她的指纹,我需要初始化密码,而密码需要经过身份验证的用户
这里怎么了

编辑:

我使用模拟器(Nexus4,API23)

下面是我用来创建密钥的代码

private SecretKey createKey() {
    try {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
        keyGenerator.init(new KeyGenParameterSpec.Builder(
                KEY_NAME,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
        )
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setUserAuthenticationRequired(true)
                .setUserAuthenticationValidityDurationSeconds(AUTHENTICATION_DURATION_SECONDS)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .build());
        return keyGenerator.generateKey();
    } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
        throw new RuntimeException("Failed to create a symmetric key", e);
    }
}

事实证明,该问题与
KeyGenParameterSpec
的一个已知问题有关,该问题阻止在没有身份验证的情况下使用公钥(这正是公钥不需要的)

相关问题/答案可在此处找到:

解决方法是从最初创建的密钥创建一个
公钥
,并使用此不受限制的公钥初始化密码。 因此,我的最终密码使用AES/CBC/PKCS7Padding,并通过以下方法初始化:

public boolean initCipher(int opMode) {
    try {
        Key key = mKeyStore.getKey(KEY_NAME, null);

        if (opMode == Cipher.ENCRYPT_MODE) {
            final byte[] encoded = key.getEncoded();
            final String algorithm = key.getAlgorithm();
            final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
            PublicKey unrestricted = KeyFactory.getInstance(algorithm).generatePublic(keySpec);

            mCipher.init(opMode, unrestricted);

        } else {
            final IvParameterSpec ivParameterSpec = getIvParameterSpec();
            mCipher.init(opMode, key, ivParameterSpec);

        }

        return true;

    } catch (KeyPermanentlyInvalidatedException exception) {
        return false;

    } catch ( NoSuchAlgorithmException | InvalidKeyException
            | InvalidKeySpecException | InvalidAlgorithmParameterException | UnrecoverableKeyException | KeyStoreException exception) {
        throw new RuntimeException("Failed to initialize Cipher or Key: ", exception);
    }
}

@NonNull
public IvParameterSpec getIvParameterSpec() {
    // the IV is stored in the Preferences after encoding.
    String base64EncryptionIv = PreferenceHelper.getEncryptionIv(mContext);
    byte[] encryptionIv = Base64.decode(base64EncryptionIv, Base64.DEFAULT);
    return new IvParameterSpec(encryptionIv);
}

我找到了逃脱第二十二条军规的方法

你是这样做的:

public static Cipher getDecryptionCipher(Context context) throws KeyStoreException {
    try {
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        SecretKey secretKey = getKeyFromKeyStore();
        final IvParameterSpec ivParameterSpec = getIvParameterSpec(context);

        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        return cipher;

    } catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | UnrecoverableKeyException | CertificateException | InvalidAlgorithmParameterException | InvalidKeyException e) {
        e.printStackTrace();

    }

    return null;
}

Cipher cipher = FingerprintCryptoHelper.getDecryptionCipher(getContext());
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
fingerprintManager.authenticate(cryptoObject, ...);
您可以像往常一样尝试{
.init
yout
Cipher

  • 如果没有
    UserNotAuthenticatedException
    (因为用户在您的密钥有效期内进行了身份验证,即因为他在几秒钟前解锁了他的设备),则执行加密/解密例程。结束

  • 您捕获了
    UserNotAuthenticatedException
    -运行
    FingerprintManager。使用
    null
    (是!)
    CryptoObject
    对工作流进行身份验证,然后在
    onauthenticationsuccessed
    中再次回调初始化密码(是!),但这次它不会抛出
    UserNotAuthenticatedException
    并使用此初始化实例进行加密/解密(回调返回
    null
    ,我们用
    null
    CryptoObject称之为
    null
    ,因此我们不能使用它)。结束

  • 就这么简单


    但是我花了两天的时间通过跟踪和错误找到了这个方法。更不用说-似乎所有在线的身份验证示例都是错误的!

    您在哪台设备上测试这个方法?您是如何生成密钥的?并且您在模拟器中注册了至少一个指纹?@Michael它在各种设备上进行了测试,包括Nexus设备是的,我也在模拟器上测试了它。这真的有效吗?你使用AES,这是一个没有公钥的对称算法。如何从AES密钥生成公钥?它确实有效,但我无法解释为什么。我的想法和你提到的一样,但这是唯一的方法是的,我说的是“按计划”。请随意改进此方法!我不认为这可能适用于AES密钥。首先,对于
    SecretKey
    /
    私钥
    ,很有可能
    getEncoded
    会返回
    null
    ,以避免此类密钥材料暴露在t形三通外面。即使这里不是这样,
    KeyFactory.getInstance
    对于AES应该会失败,因为它只支持DH、DSA、EC、RSA和X.509。在我看来,您实际上在使用其他密钥。它确实返回null,indeed是否有人可以确认这一点,然后我会将其标记为正确答案它工作正常,刚刚尝试并避免了雏鸡问题!谢谢