Ivan Budnikov
Ivan Budnikov

Reputation: 383

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?

Upvotes: 4

Views: 6691

Answers (8)

agomes
agomes

Reputation: 331

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.

Upvotes: 1

Seyed Masood Khademi
Seyed Masood Khademi

Reputation: 173

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;

Upvotes: 0

Hitesh Sahu
Hitesh Sahu

Reputation: 45080

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

Upvotes: 4

SWAROOP REDDY
SWAROOP REDDY

Reputation: 1

I solved this by moving all the fingerprint code to a helper class so that the classes related to the fingerprint code are not imported in the activity and by instantiating the helper class only when the SDK_INT is greater than 23 (In my case, as I'm supporting only Android 6+)

Upvotes: 0

liangliangli
liangliangli

Reputation: 11

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);
    }

Upvotes: 1

Hilit
Hilit

Reputation: 65

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 i m 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.

Upvotes: 5

Ivan Budnikov
Ivan Budnikov

Reputation: 383

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...

Upvotes: 14

t0mm13b
t0mm13b

Reputation: 34592

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.
}

Upvotes: 0

Related Questions