Mobile Development 19 min read

How to Implement Secure Fingerprint Login on Android with BiometricPrompt and Keystore

This article walks through implementing fingerprint login on Android, covering the legacy FingerprintManager API, the newer BiometricPrompt, integrating with Android Keystore for secure encryption, handling compatibility, UI considerations, and security best practices, complete with code examples and deployment results.

Huolala Tech
Huolala Tech
Huolala Tech
How to Implement Secure Fingerprint Login on Android with BiometricPrompt and Keystore

1. Introduction

Fingerprint login is a common method in finance apps; we added it to the Huolala driver Android app to improve user experience and reduce SMS costs. Google provides fingerprint APIs starting from Android 6.0.

2. Fingerprint Recognition

From Android 6.0 (API23) the system supports fingerprint via FingerprintManager. The API allows checking hardware, enrolled fingerprints, starting authentication, cancelling, and receiving callbacks. From Android 9.0 (API28) FingerprintManager is deprecated in favor of BiometricPrompt, which supports multiple biometrics but currently only fingerprint on most devices and enforces a standard UI.

2.1 FingerprintManager

Steps to use FingerprintManager:

① Request fingerprint permission

<!-- Android 6.0 enable touch sensor and identity authentication permission -->
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>

② Check if fingerprint is supported

isHardwareDetected() – hardware support

isKeyguardSecure() – lock screen set

hasEnrolledFingerprints() – at least one fingerprint enrolled

/**
 * Determine if fingerprint is supported
 */
public static boolean supportFingerprint(Context context) {
    if (VERSION.SDK_INT < 23) {
        // System version too low
        return false;
    } else {
        KeyguardManager km = context.getSystemService(KeyguardManager.class);
        FingerprintManagerCompat fm = FingerprintManagerCompat.from(context);
        if (!fm.isHardwareDetected()) {
            return false;
        } else if (!km.isKeyguardSecure()) {
            return false;
        } else if (!fm.hasEnrolledFingerprints()) {
            return false;
        }
    }
    return true;
}

③ Start authentication

@RequiresApi(api = Build.VERSION_CODES.M)
public void authenticate(FingerprintManager.CryptoObject cryptoObject,
                         CancellationSignal cancellationSignal,
                         int flag,
                         FingerprintManager.AuthenticationCallback authenticationCallback,
                         Handler handler) {
    if (fingerprintManager != null) {
        fingerprintManager.authenticate(cryptoObject, cancellationSignal, flag, authenticationCallback, handler);
    }
}

Authentication callbacks include onAuthenticationFailed, onAuthenticationSucceeded, onAuthenticationHelp, and onAuthenticationError. The event flow is: start → (help/failed)* → succeeded/error.

④ Cancel authentication

/**
 * Stop listening
 */
public void cancelListening() {
    if (cancellationSignal != null) {
        cancellationSignal.cancel();
    }
}

2.2 BiometricPrompt

Differences from FingerprintManager:

Add dependency: implementation "androidx.biometric:biometric:1.1.0" Request biometric permission:

<!-- Android 9.0 biometric permission -->
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>

③ Launch authentication

BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
    .setTitle("Fingerprint verification")
    .setDescription("User fingerprint verification")
    .setNegativeButtonText("Cancel")
    .build();
getPrompt().authenticate(promptInfo);

private BiometricPrompt getPrompt() {
    Executor executor = ContextCompat.getMainExecutor(this);
    BiometricPrompt.AuthenticationCallback callback = new BiometricPrompt.AuthenticationCallback() {
        @Override
        public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
            // success
        }
        @Override
        public void onAuthenticationFailed() {
            // failure
        }
        @Override
        public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
            // error
        }
    };
    return new BiometricPrompt(this, executor, callback);
}

④ UI – system dialog cannot be customized; example on Honor X8:

BiometricPrompt UI
BiometricPrompt UI

Limitations: only success/failure is returned, no account binding, and root/hook attacks can bypass authentication.

2.3 Android Keystore

Android Keystore stores keys in a secure container (TEE). Keys can be used for encryption without being exported. Combining Keystore with fingerprint ensures the key is usable only after successful biometric authentication.

Steps:

① Create KeyStore instance

KeyStore keystore = KeyStore.getInstance(KEYSTORE_NAME);
keystore.load(null);

② Generate AES key with user authentication required

@RequiresApi(api = Build.VERSION_CODES.M)
public void createKey() throws Exception {
    KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
    KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setUserAuthenticationRequired(true)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
    keyGenerator.init(builder.build());
    keyGenerator.generateKey();
}

③ Initialize Cipher with the key to obtain CryptoObject

public Cipher createCipher() throws Exception {
    SecretKey key = (SecretKey) keyStore.getKey(DEFAULT_KEY_NAME, null);
    Cipher cipher = Cipher.getInstance(
        KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return cipher;
}

④ Start fingerprint authentication with the CryptoObject

CryptoObjectHelper cryptoObjectHelper = new CryptoObjectHelper();
if (cancellationSignal == null) {
    cancellationSignal = new CancellationSignal();
}
fingerprintManager.authenticate(
    cryptoObjectHelper.buildCryptoObject(),
    0,
    cancellationSignal,
    myAuthCallback,
    null);

⑤ In onAuthenticationSucceeded, retrieve the Cipher and perform encryption

@Override
public void onSucceeded(FingerprintManager.AuthenticationResult result) {
    try {
        Cipher cipher = result.getCryptoObject().getCipher();
        byte[] bytes = cipher.doFinal(pwd.getBytes());
        aCache.put("pwdEncode", Base64.encodeToString(bytes, Base64.URL_SAFE));
        byte[] iv = cipher.getIV();
        aCache.put("iv", Base64.encodeToString(iv, Base64.URL_SAFE));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

3. Fingerprint Login

Fingerprint login replaces password entry by encrypting login data locally with a key protected by biometric authentication.

3.1 Enable Fingerprint Login

Process: after a normal login, enable fingerprint, compare stored fingerprints (kept locally), and store a private key in Keystore; the public key is uploaded for server verification.

Enable fingerprint login flow
Enable fingerprint login flow

3.2 Use Fingerprint Login

When logging in, retrieve encrypted data, send to server for decryption and verification.

Fingerprint login flow
Fingerprint login flow

Additional measures: one‑time authorization code and signature with a client‑side private key bound to biometric authentication.

4. Compatibility

4.1 Version Compatibility

Fingerprint APIs are available from Android 6.0. Below that, manufacturers may provide proprietary SDKs. FingerprintManager is deprecated after Android 9.0; developers should handle both FingerprintManagerCompat and BiometricPrompt checks.

4.2 UI Compatibility

FingerprintManager

allows custom UI, while BiometricPrompt uses a system dialog whose appearance varies across manufacturers.

5. Security

Rooted devices can bypass fingerprint verification. Using Android Keystore to generate a key and passing it as CryptoObject ensures the key is usable only after successful biometric authentication.

Google’s API does not expose fingerprint IDs, so binding a fingerprint to a specific account is not possible; new fingerprints added later can also unlock the same account.

Some low‑level methods to obtain fingerprint IDs are blocked by @UnsupportedAppUsage.

Third‑party solutions such as Tencent’s SOTER and Alibaba’s IFAA provide hardware‑rooted keys stored in TEE, improving security for high‑risk scenarios like payments.

6. Deployment Results

After release, the fingerprint login feature ran on Honor 8X devices, as shown in the screenshots:

Screenshot 1
Screenshot 1
Screenshot 2
Screenshot 2
Screenshot 3
Screenshot 3
Screenshot 4
Screenshot 4

7. Summary

Since launch, many drivers have enabled fingerprint login, improving user experience and reducing SMS verification costs by about 20%. Sharing the implementation details helps other developers avoid pitfalls.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidFingerprintkeystoreBiometricPrompt
Huolala Tech
Written by

Huolala Tech

Technology reshapes logistics

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.