Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Face authentication to unlock my app programmatically

I am developing an android app which uses fingerprint/face recognition for unlocking the app.

I have successfully integrated fingerprint authentication using BiometricPrompt. But didn't know where to start for Face authentication. Any headsup will be really helpful.

Also, Since BiometricPrompt comes with face, fingerprint, and iris, I don't want to use either MLKIT or any third party libraries.

Below is the piece of code I used for Fingerprint authentication.

new BiometricPrompt
    .Builder(context)
    .setTitle(title)
    .setSubtitle(subtitle)
    .setDescription(description)
    .setNegativeButton(negativeButtonText, context.getMainExecutor(),
                        new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
            biometricCallback.onAuthenticationCancelled();
        }
    })
    .build()
    .authenticate(new CancellationSignal(), context.getMainExecutor(),
            new BiometricCallbackV28(biometricCallback));
like image 524
Kanagalingam Avatar asked Dec 12 '18 08:12

Kanagalingam


2 Answers

Android 10: Will allow both finger print and Face ID as currently in Samsung s10.

Android 9: Will allow only Fingerprint authentication (irrespective of face Id unlock exist or not )

reference link https://source.android.com/security/biometric/#source

Edit1: However the Samsung is not following google's ritual. Samsung will still have different behaviours .

1.Samsung - Android 9 - you can choose preferred biometrics and this applicable for the apps well.

But there is a bug. If you disable the finger print authentication for device level unlock you will get prompted for the fingerprint authentication in the app level.

  1. Samsung - Android 10 - you can keep all the authentications active at same time. In biometric prompt you can choose authentication.

Short summary :

enter image description here

like image 150
saranhya narakedamilli Avatar answered Nov 19 '22 04:11

saranhya narakedamilli


Step1: Add the latest biometric dependency in build.gradle file.

implementation 'androidx.biometric:biometric:1.0.1' 

Step2: Add permission for biometric in AndroidManifest.xml file.

<uses-permission android:name="android.permission.USE_BIOMETRIC" />

Step3: Create activity_login.xml file and define the Touch ID button's UI.

activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
        android:id="@+id/parent_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="30dp">

        <Button
            android:id="@+id/touch_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/river_blue"
            android:stateListAnimator="@null"
            android:text="Biometric Login"
            android:textAllCaps="false"
            android:textColor="@color/white"
            android:textSize="16sp" />

    </RelativeLayout>

</layout>

Step4: Create LoginActivity.java file to implement the biometric login feature.

LoginActivity.java:

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;

import android.os.Build;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.widget.Button;

import androidx.biometric.BiometricManager;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

/**
 * Created by: rajoo.kannaujiya on 02/16/2020
 */
@RequiresApi(api = Build.VERSION_CODES.P)
public class LoginActivity extends AppCompatActivity {
    private static final String KEY_NAME = "KeyName";
    private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
    private static final String FORWARD_SLASH = "/";
    private Button touchButton;

    @RequiresApi(api = Build.VERSION_CODES.P)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        touchButton = findViewById(R.id.touch_button);
        touchButton.setOnClickListener((view) -> onTouchIdClick());
        displayBiometricButton();
    }

    private void onTouchIdClick() {
        getBiometricPromptHandler().authenticate(getBiometricPrompt(), new BiometricPrompt.CryptoObject(getCipher()));
        // Please see the below mentioned note section.
        // getBiometricPromptHandler().authenticate(getBiometricPrompt());
    }

    private boolean isBiometricCompatibleDevice() {
        if (getBiometricManager().canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
            return true;
        } else {
            return false;
        }
    }

    private void displayBiometricButton() {
        if (isBiometricCompatibleDevice()) {
            touchButton.setEnabled(false);
        } else {
            touchButton.setEnabled(true);
            generateSecretKey();
        }
    }

    private BiometricManager getBiometricManager() {
        return BiometricManager.from(this);
    }

    private void generateSecretKey() {
        KeyGenerator keyGenerator = null;
        KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
                KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .setUserAuthenticationRequired(true)
                .setInvalidatedByBiometricEnrollment(false)
                .build();
        try {
            keyGenerator = KeyGenerator.getInstance(
                    KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            e.printStackTrace();
        }
        if (keyGenerator != null) {
            try {
                keyGenerator.init(keyGenParameterSpec);
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            }
            keyGenerator.generateKey();
        }
    }

    private SecretKey getSecretKey() {
        KeyStore keyStore = null;
        Key secretKey = null;
        try {
            keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        if (keyStore != null) {
            try {
                keyStore.load(null);
            } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            try {
                secretKey = keyStore.getKey(KEY_NAME, null);
            } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
                e.printStackTrace();
            }
        }
        return (SecretKey) secretKey;
    }

    private Cipher getCipher() {
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + FORWARD_SLASH
                    + KeyProperties.BLOCK_MODE_CBC + FORWARD_SLASH
                    + KeyProperties.ENCRYPTION_PADDING_PKCS7);
            try {
                cipher.init(Cipher.ENCRYPT_MODE, getSecretKey());
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            }
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            e.printStackTrace();
        }
        return cipher;
    }

    private BiometricPrompt.PromptInfo getBiometricPrompt() {
        return new BiometricPrompt.PromptInfo.Builder()
                .setTitle("Biometric login for my app")
                .setSubtitle("Login with your biometric credential")
                .setNegativeButtonText("cancel")
                .setConfirmationRequired(false)
                .build();
    }

    private void onBiometricSuccess() {
        //Call the respective API on biometric success
        callLoginApi("userName", "password");
    }

    private BiometricPrompt getBiometricPromptHandler() {
        return new BiometricPrompt(this, ContextCompat.getMainExecutor(this),
                new BiometricPrompt.AuthenticationCallback() {

                    @Override
                    public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
                        super.onAuthenticationError(errorCode, errString);
                    }

                    @Override
                    public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
                        super.onAuthenticationSucceeded(result);
                        onBiometricSuccess();
                    }

                    @Override
                    public void onAuthenticationFailed() {
                        super.onAuthenticationFailed();
                    }
                }
        );
    }
}

Note: The authentication level for biometric sensors (fingerprint, face, iris) are classified as Strong and Weak. As per Android Compatibility Definition Document (Android CDD), To access the android key store, the authentication level for biometric sensors must be classified as Strong. Since face and iris sensor are classified as Weak category in some of the devices, Hence in those devices face and iris options will not be displayed in the biometric prompt authenticated with CryptoObject.

getBiometricPromptHandler().authenticate(getBiometricPrompt(), new BiometricPrompt.CryptoObject(getCipher()));

If you authenticate the biometric prompt without CryptoObject, Then it will be displayed.

getBiometricPromptHandler().authenticate(getBiometricPrompt());
like image 36
Rajoo Kannaujiya Avatar answered Nov 19 '22 04:11

Rajoo Kannaujiya