简体   繁体   中英

Android fingerprint on SDK<23

I have a project on Android with minSDK=17 and targetSDK=23. We have a fingerprint authentication in this project made using FingerprintManager class (it was added in SDK23). We added SDK version check, so we are not using anything related to fingerprint if SDK<23. But in older SDK versions app behaviour is unpredictable: on some versions app just crashing, on other -- fingerprint not working (so, it's ok).

My question:

1) Is it any good and easy-to-implement libraries for minSDK=17, that can recognize fingerprints? 2) How can I avoid app crashing in devices with SDK<23? Crash error:

E/dalvikvm: Could not find class 'android.hardware.fingerprint.FingerprintManager', referenced from method nl.intratuin.LoginActivity.loginByFingerprint
E/AndroidRuntime: FATAL EXCEPTION: main   java.lang.VerifyError:
LoginActivity at java.lang.Class.newInstanceImpl(Native Method)

Some new info: created HelloWorld fingerprint project using this tutorial: http://www.techotopia.com/index.php/An_Android_Fingerprint_Authentication_Tutorial Found the root of the problem: FingerprintDemoActivity->cipherInit:

try {
    keyStore.load(null);
    SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
             null);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return true;
} catch (KeyPermanentlyInvalidatedException e) {
    return false;
} catch (KeyStoreException | CertificateException 
      | UnrecoverableKeyException | IOException
      | NoSuchAlgorithmException | InvalidKeyException e) {
   throw new RuntimeException("Failed to init Cipher", e);
}

First catch block breacking whole app with error I mentioned above. Of course, I can just remove this catch (this exception extends InvalidKeyException, so it will be handled), and return false in case of any exceptions. Is it any better way?

I think, I found acceptable solution: catch not KeyPermanentlyInvalidatedException, but InvalidKeyException. Everything working fine this way. Still have no idea how this exception crashed whole app...

It happened to me also..even when i used : if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M).. my app crashes on android 4.4- kitkat. so eventually the problem was in the initCipher method in the catches part - see the following code (even though im not suppose to get there as it targeted to M and above... very strange behaviour..) :

@TargetApi(Build.VERSION_CODES.M)
private boolean initCipher() {
    try {
        mKeyStore.load(null);
        SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
        mCipher.init(Cipher.ENCRYPT_MODE, key);

        return true;
    } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
            | NoSuchAlgorithmException e) {
        throw new RuntimeException("Failed to init Cipher", e);
    } catch (InvalidKeyException e) {
        e.printStackTrace();
        return false;

    }
}

apparently the order off the catches matter..so just make sure to write it as i mentioned.

Reason for Crash:

The FingerprintManager class works with Android version 23 and Higher.

If your app is using FingerprintManager class and runs on older version of Android then you will encounter this exception.

Supporting older versions of Android:

Use FingerprintManagerCompat instead of FingerprintManager if you are planning to support Android <23 . The FingerprintManagerCompat class internally checks for the Android version and handle Authentication part with ease.

How to Use it:

  • Replace android.hardware.fingerprint.FingerprintManager With android.support.v4.hardware.fingerprint.FingerprintManagerCompat

  • Replace android.os.CancellationSignal With android.support.v4.os.CancellationSignal

See Sample Code

https://github.com/hiteshsahu/FingerPrint-Authentication-With-React-Native-Android/blob/master/android/app/src/main/java/com/aproject/view/Fragments/FingerprintAuthenticationDialogFragment.java

Have a look at a library created by afollestad called digitus . This library can fall back to a password if fingerprints are not available.

Any devices prior to SDK 23 need to use their own separate device manufacturer based sdk.

just follow android studio hint, it will be OK.

 try {
        mKeyStore.load(null);
        SecretKey key = (SecretKey) mKeyStore.getKey(keyName, null);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return true;
    } catch (IOException | NoSuchAlgorithmException | CertificateException
            | UnrecoverableKeyException | KeyStoreException | InvalidKeyException e) {
        e.printStackTrace();
        throw new RuntimeException("Failed to init Cipher", e);
    }

To answer the second part of the question

How can I avoid app crashing in devices with SDK<23?

This simplistic logic check will suffice:

if (Build.VERSION.SDK_INT < 23) { 
   // Handle the mechanism where the SDK is older.
}else{
   // Handle the mechanism where the SDK is 23 or later.
}

我通过将所有指纹代码移动到一个助手类来解决这个问题,这样与指纹代码相关的类就不会在活动中导入,并且仅在 SDK_INT 大于 23 时才实例化助手类(在我的情况下,因为我m 仅支持 Android 6+)

I also had this problem.Even when i used : if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M).. my app crashes on lower API's. I solved that as follow: Replaced these codes:

      try {

            keyStore.load(null);
            SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
                    null);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return true;
        } catch (KeyPermanentlyInvalidatedException e) {
            return false;
        } catch (KeyStoreException | CertificateException
                | UnrecoverableKeyException | IOException
                | NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException("Failed to init Cipher", e);
        }

with:

     try {

            keyStore.load(null);
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        SecretKey key = null;
        try {
            key = (SecretKey) keyStore.getKey(KEY_NAME,
                    null);
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        }
        try {
            cipher.init(Cipher.ENCRYPT_MODE, key);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return true;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM