There are a number of ways to authenticate a user to access an application, or a certain feature within an application, such as checkout, and fingerprint is one of them. For this post instead of going through the code step by step, i have added in-code comments, and will be letting the code speak for itself.
First things first, the permission.
The good thing is that we do not need to ask for this permission at runtime.
This project only has one activity, the MainActivity. Lets take a look at the code.
public class MainActivity extends AppCompatActivity { // Variable used for storing the key in the Android Keystore container private static final String KEY_STORE_ALIAS = "key_fingerprint"; private static final String KEY_STORE = "AndroidKeyStore"; private KeyStore keyStore; private Cipher cipher; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Get an instance of the fingerprint manager through the getSystemService method final FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE); // Our fingerprint checker final FingerPrintChecker checker = new FingerPrintChecker(this, fingerprintManager); if (checker.isAbleToUseFingerPrint()) { generateAuthenticationKey(); if (isCipherInitialized()) { // A wrapper for the crypto objects supported by the FingerprintManager final FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher); // Our fingerprint callback helper final FingerprintHelper fingerprintHelper = new FingerprintHelper(this); fingerprintHelper.startAuthentication(fingerprintManager, cryptoObject); } } } /** * Generates the authentication key required to use with the {@link FingerprintManager} to * encrypt/decrypt fingerprints. */ @TargetApi (Build.VERSION_CODES.M) private void generateAuthenticationKey() { getKeyStoreInstance(); final KeyGenerator keyGenerator = getKeyGenerator(); try { keyStore.load(null); final KeyGenParameterSpec parameterSpec = getKeyGenParameterSpec(); // Initialize the key generator keyGenerator.init(parameterSpec); // Generate the key. This also returns the generated key for immediate use if needed. // For this example we will grab it later on. keyGenerator.generateKey(); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertificateException | IOException e) { throw new RuntimeException(e); } } /** * Generate the {@link KeyGenParameterSpec} required for us to encrypt/decrypt. */ @NonNull private KeyGenParameterSpec getKeyGenParameterSpec() { // Specify what we are trying to do with the generated key final int purposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT; // Specifications for the key generator. How to generate the key return new KeyGenParameterSpec.Builder(KEY_STORE_ALIAS, purposes) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .build(); } /** * Get an instance of the Java {@link KeyStore} */ private void getKeyStoreInstance() { try { keyStore = KeyStore.getInstance(KEY_STORE); } catch (Exception e) { e.printStackTrace(); } } /** * Get the key generator required to generate the keys uses for encryption/decryption */ private KeyGenerator getKeyGenerator() { final KeyGenerator keyGenerator; try { keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE); } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new RuntimeException("Failed to get KeyGenerator instance", e); } return keyGenerator; } /** * Initializes the Cipher object required to perform the fingerprint authentication. * * @return True if Cipher init was successful. False otherwise. */ @TargetApi (Build.VERSION_CODES.M) private boolean isCipherInitialized() { try { // Get a cipher instance with the following transformation --> AES/CBC/PKCS7Padding cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw new RuntimeException("Failed to get cipher instance", e); } try { keyStore.load(null); // The key - This key was generated in the {@link #generateAuthenticationKey()} method final SecretKey key = (SecretKey) keyStore.getKey(KEY_STORE_ALIAS, 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); } } }
There 2 helper classes used in the code above, lets start with the FingerPrintChecker.
class FingerPrintChecker { private final Context context; private final KeyguardManager keyguardManager; private final FingerprintManager fingerprintManager; FingerPrintChecker(final Context context, final FingerprintManager fingerprintManager) { this.context = context; this.fingerprintManager = fingerprintManager; // Initializing both Android Keyguard Manager and Fingerprint Manager keyguardManager = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE); } @SuppressWarnings ("MissingPermission") boolean isAbleToUseFingerPrint() { // Check whether the device has a Fingerprint sensor. if (!fingerprintManager.isHardwareDetected()) { // Device does not support fingerprint authentication. You can take this opportunity to // redirect the user to some other authentication method or activity showMessage("Your Device does not support fingerprint authentication"); return false; } else { // Checks whether fingerprint permission is set if (isFingerprintPermissionEnabled()) { showMessage(String.format(context.getString(R.string.error_permission_missing), Manifest.permission.USE_FINGERPRINT)); return false; } else { // Check whether at least one fingerprint is registered if (!fingerprintManager.hasEnrolledFingerprints()) { showMessage("Please register at least one fingerprint in your device settings"); return false; } else { // Checks whether lock screen security is enabled or not if (!keyguardManager.isKeyguardSecure()) { showMessage("Lock screen security not enabled in your device settings"); return false; } else { return true; } } } } } private boolean isFingerprintPermissionEnabled() { return ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED; } private void showMessage(final String message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } }
Our FingerPrintHelper class
class FingerprintHelper extends FingerprintManager.AuthenticationCallback { private static final String TAG = FingerprintHelper.class.getSimpleName(); private final Context context; FingerprintHelper(@NonNull final Context context) { this.context = context; } void authenticate(@NonNull final FingerprintManager fingerprintManager, @NonNull final FingerprintManager.CryptoObject cryptoObject) { // Provides the ability to cancel an operation in progress. final CancellationSignal cancellationSignal = new CancellationSignal(); if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "Error: cannot authenticate. Permission denied."); return; } /* Request authentication of a crypto object. This call warms up the fingerprint hardware and starts scanning for a fingerprint. It terminates when {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at which point the object is no longer valid. The operation can be canceled by using the provided cancel object. */ fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, this, null); } @Override public void onAuthenticationError(int errorCode, CharSequence errString) { showMessage("Fingerprint Authentication error" + errString); } @Override public void onAuthenticationHelp(int helpCode, CharSequence helpString) { showMessage("Fingerprint Authentication help" + helpString); } @Override public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { showMessage("Fingerprint Authentication succeeded."); } @Override public void onAuthenticationFailed() { showMessage("Fingerprint Authentication failed."); } private void showMessage(String message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } }
You can find the full source code HERE
Thats all for now, until next time!
If you have any questions don’t hesitate to contact me or leave a comment!
can u be so kind and post project code 🙂 ?
thats great tutorial, but i have some problems with run it
Absolutely. You can find all of it here: https://github.com/josias1991/FingerPrintSample