Share
Embed
Transcript
64 slides
-
Why should I care? • Easy and quick • Safe
from peeking while they type in their password -
With the new feature came an API FingerprintManager Allows you
to listen for fingerprint authentication events -
BiometricPrompt API • Known as FingerprintDialog API in early versions
of P Preview (as at the time of submission of this talk) -
BiometricPrompt API • Known as FingerprintDialog API in early versions
of P Preview (as at the time of submission of this talk) • Provide consistent and easy API for developers -
BiometricPrompt API • Known as FingerprintDialog API in early versions
of P Preview (as at the time of submission of this talk) • Provide consistent and easy API for developers • Provide consistent system UI that is sensor-agnostic -
Symmetric vs Asymmetric Keystore Symmetric: aka “secret key” - same
key for encryption and decryption Asymmetric: aka “public key” - different keys for encryption and decryption https://www.cs.utexas.edu/users/byoung/cs361/lecture44.pdf -
Android Keystore Hardware backed keystore that lets you store cryptographic
keys in a container and makes it more difficult to extract from the device. -
Cipher Loosely speaking - it’s the algorithm used to encrypt/decrypt
data Usually a set of operations or transformations to be performed on inputs -
0. Request permission <manifest ... <uses-permission android:name="android.permission.USE_FINGERPRINT" /> </manifest> Previously,
requesting permission looked like: <manifest ... <uses-permission android:name="android.permission.USE_BIOMETRIC" /> </manifest> New permission: -
1. Check device requirements if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //
Proceed with fingerprint authentication } else { // Fallback to password or manual authentication } -
Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =
getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} -
Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =
getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} -
Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =
getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} -
Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =
getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} // Proceed with auth -
Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =
getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} // Proceed with auth Deprecated -
Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =
getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} // Proceed with auth New API offers callbacks to handle errors and unsatisfied criteria -
Create key store from the AndroidKeyStore // Create Keystore try
{ keyStore = KeyStore.getInstance("AndroidKeyStore") } catch (e: KeyStoreException) { throw RuntimeException("Failed to get an instance of KeyStore", e) } 2. Create & initialize a cipher -
Create KeyGenerator that will be used to create the symmetric
key // Create Keystore try { keyStore = KeyStore.getInstance("AndroidKeyStore") } catch (e: KeyStoreException) { throw RuntimeException("Failed to get an instance of KeyStore", e) } // Create KeyGenerator (this will be used to create the key) try { keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM_AES, "AndroidKeyStore") } catch (e: Exception) { when (e) { is NoSuchAlgorithmException, is NoSuchProviderException -> throw RuntimeException(...) else -> throw e } 2. Create & initialize a cipher -
Use KeyGenerator to generate keys try { keyStore.load(null) val keyProperties
= KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT val builder = KeyGenParameterSpec.Builder(“default_key”, keyProperties) .setBlockModes(BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7) .setInvalidatedByBiometricEnrollment(true) // generate keys keyGenerator.init(builder.build()) keyGenerator.generateKey() } catch (e: Exception) { // handle exceptions } -
Use KeyGenerator to generate keys try { keyStore.load(null) val keyProperties
= KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT val builder = KeyGenParameterSpec.Builder(“default_key”, keyProperties) .setBlockModes(BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7) .setInvalidatedByBiometricEnrollment(true) // generate keys keyGenerator.init(builder.build()) keyGenerator.generateKey() } catch (e: Exception) { // handle exceptions } Key is authorized to be used only when user has been authenticated (e.g unlocked phone) -
Key is invalidated when a new fingerprint is added Use
KeyGenerator to generate keys try { keyStore.load(null) val keyProperties = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT val builder = KeyGenParameterSpec.Builder(“default_key”, keyProperties) .setBlockModes(BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7) .setInvalidatedByBiometricEnrollment(true) // generate keys keyGenerator.init(builder.build()) keyGenerator.generateKey() } catch (e: Exception) { // handle exceptions } -
Create a cipher // Create a cipher val cipher :
Cipher try { val cipherString = "$KEY_ALGORITHM_AES/$BLOCK_MODE_CBC/$ENCRYPTION_PADDING_PKCS7" cipher = Cipher.getInstance(cipherString) } catch (e: Exception) { when (e) { is NoSuchAlgorithmException, is NoSuchPaddingException -> throw RuntimeException("Failed to get an instance of Cipher", e) else -> throw e } } -
Initialize the cipher fun initCipher(cipher: Cipher): Boolean { try {
keyStore.load(null) val secretKey = keyStore.getKey("key_name", null) as SecretKey // init cipher.init(Cipher.ENCRYPT_MODE, secretKey) return true } catch (e: Exception) { when (e) { is KeyPermanentlyInvalidatedException -> return false ... else -> throw e } } } -
Initialize the cipher fun initCipher(cipher: Cipher): Boolean { try {
keyStore.load(null) val secretKey = keyStore.getKey("default_key", null) as SecretKey // init cipher.init(Cipher.ENCRYPT_MODE, secretKey) return true } catch (e: Exception) { when (e) { is KeyPermanentlyInvalidatedException -> return false ... else -> throw e } } } Unrecoverably invalidated due to - change in security settings of the phone, or since we configured setInvalidatedByBiometricEnrollment to enrollment of new fingerprint -
val cancellationSignal = new CancellationSignal(); val cryptoObject = FingerprintManager.CryptoObject(cipher) //
Warm up the sensor and start listening for authentication val manager = getSystemService(FingerprintManager::class.java) manager.authenticate(cryptoObject, cancellationSignal, 0, callback, null); 3. Listen for fingerprint auth events Authenticating with the old FingerprintManager -
val cancellationSignal = new CancellationSignal(); val cryptoObject = FingerprintManager.CryptoObject(cipher) //
Warm up the sensor and start listening for authentication val manager = getSystemService(FingerprintManager::class.java) manager.authenticate(cryptoObject, cancellationSignal, 0, callback, null); 3. Listen for fingerprint auth events Authenticating with the old FingerprintManager Deprecated -
Authenticating with the new APIs // Create builder val builder
= BiometricPrompt.Builder(this) .setTitle("Confirm purchase") .setSubtitle("Currywurst - €5,50") .setDescription("Touch the fingerprint sensor....") .setNegativeButton("Cancel", mainExecutor, object : DialogInterface.OnClickListener { override fun onClick(dialog: DialogInterface?, p1: Int) { // handle cancel action } }) .build() 3. Listen for fingerprint auth events -
Authenticating with the new APIs // Create builder val builder
= BiometricPrompt.Builder(this) .setTitle("Confirm purchase") .setSubtitle("Currywurst - €5,50") .setDescription("Touch the fingerprint sensor....") .setNegativeButton("Cancel", mainExecutor, object : DialogInterface.OnClickListener { override fun onClick(dialog: DialogInterface?, p1: Int) { // handle cancel action } }) .build() // Authenticate and listen for events builder.authenticate(BiometricPrompt.CryptoObject(cipher), cancellationSignal, mainExecutor, callback) -
Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override
fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } } -
Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override
fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } } -
Handling results - error codes BIOMETRIC_ERROR_CANCELED BIOMETRIC_ERROR_HW_NOT_PRESENT BIOMETRIC_ERROR_HW_UNAVAILABLE BIOMETRIC_ERROR_LOCKOUT BIOMETRIC_ERROR_LOCKOUT_PERMANENT
BIOMETRIC_ERROR_NO_BIOMETRICS BIOMETRIC_ERROR_NO_SPACE BIOMETRIC_ERROR_TIMEOUT BIOMETRIC_ERROR_UNABLE_TO_PROCESS BIOMETRIC_ERROR_USER_CANCELED BIOMETRIC_ERROR_VENDOR -
Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override
fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } } -
Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override
fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } } -
Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override
fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } } -
Last words... • Provide alternative ways to authenticate (password, OTP,
and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app -
Last words... • Provide alternative ways to authenticate (password, OTP,
and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them -
Last words... • Provide alternative ways to authenticate (password, OTP,
and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them • Ensure good API practices with signed requests -
Last words... • Provide alternative ways to authenticate (password, OTP,
and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them • Ensure good API practices with signed requests • The APIs may not be final yet -
Last words... • Provide alternative ways to authenticate (password, OTP,
and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them • Ensure good API practices with signed requests • The APIs may not be final yet • No Compat APIs yet