Sensitive data Personal life information, physical or mental health details, criminal or civil offences, private photos, private user documents, etc. #dfua sh7aertsca.. Сipher data RSA EC AES DES Financial data Credentials Algorithm Asymmetric Symmetric Plain data
key Public key Key Accounts, transactions, reports, credit card information, etc. RSA EC AES DES Credentials Algorithm Asymmetric Symmetric Plain data Sensitive data Financial data
Сipher data Secret key Private key Public key Key Usernames, passwords, touch pincodes, fingerprint data, and all other stuff that can provide access to data above. RSA EC AES DES Algorithm Asymmetric Symmetric Plain data
key and the decryption key are the same. #dfua sh7aertsca.. Сipher data Secret key Private key Public key Key RSA EC AES DES Financial data Credentials Sensitive data Plain data Asymmetric Algorithm Symmetric
of cryptography. Also known as public-key cryptography in which the algorithms employ a pair of keys (a public key and a private key) and use a different component of the pair for different steps of the algorithm. Secret key Private key Public key Key RSA EC AES DES Financial data Credentials Sensitive data Plain data Algorithm Asymmetric Symmetric
single secret key which is used in conventional symmetric encryption to encrypt and decrypt a message. Symmetric #dfua sh7aertsca.. Сipher data RSA EC AES DES Asymmetric Financial data Credentials Sensitive data Plain data Algorithm Key
in conventional symmetric encryption which is used to encrypt and decrypt a message. #dfua sh7aertsca.. Сipher data Secret key RSA EC AES DES Symmetric Financial data Credentials Sensitive data Plain data Algorithm Asymmetric Private key Public key Key
cryptographic keys used for encryption in asymmetric cryptography. Asymmetric #dfua sh7aertsca.. Сipher data Secret key RSA EC AES DES Symmetric Financial data Credentials Sensitive data Plain data Algorithm Private key Public key Key
engine class which is capable of generating a private key and its related public key utilizing the algorithm it was initialized with. #dfua Cipher Secure Random Provider
Keys #dfua Cipher Certificate Database with a well secured mechanism of data protection, that is used to save, get and remove keys. Requires entrance password and passwords for each of the keys.
Key material never enters the application process Key material may be bound to the secure hardware Asymmetric keys available from 18 + Symmetric keys available from 23 + #dfua
application only if Lock Screen is set Protect user password with Encryption Protect user Secrets with Encryption Allow user to access Secrets with Fingerprint Add additional Confirm Credentials protection #dfua
APIs. Guides with more detailed information (what need to be done to complete the stage and full code snippets) are placed in Readme file . On each Stage you need to listen for explanations from Slide and follow the Readme Guides in parallel. If something went wrong, you can apply any of Stages or Levels instantly by changing the flavor.
= if (hasMarshmallow()) keyguardManager.isDeviceSecure else keyguardManager.isKeyguardSecure fun hasMarshmallow(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M Device Lock Screen SystemServices class #dfua System Service that is used to check if device/keyguard is secure.
= if (hasMarshmallow()) keyguardManager.isDeviceSecure else keyguardManager.isKeyguardSecure fun hasMarshmallow(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M Device Lock Screen SystemServices class #dfua Returns whether the device is secured with a PIN, pattern or password.
= if (hasMarshmallow()) keyguardManager.isDeviceSecure else keyguardManager.isKeyguardSecure fun hasMarshmallow(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M Device Lock Screen SystemServices class #dfua Return whether the keyguard is secured by a PIN, pattern or password or a SIM card is currently locked.
= createAndroidKeyStore() private fun createAndroidKeyStore(): KeyStore { val keyStore = KeyStore.getInstance("AndroidKeyStore") keyStore.load(null) return keyStore } Use simple factory method to get a KeyStore instance
= createAndroidKeyStore() private fun createAndroidKeyStore(): KeyStore { val keyStore = KeyStore.getInstance("AndroidKeyStore") keyStore.load(null) return keyStore } With specific Provider implementation
= createAndroidKeyStore() private fun createAndroidKeyStore(): KeyStore { val keyStore = KeyStore.getInstance("AndroidKeyStore") keyStore.load(null) return keyStore } And finally create new Keystore
= KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair()
= KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair() Factory method to get Key Pair Generator instance
= KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair() Where we need to specify the Algorithm we want
= KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair() val generator = KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair() And implementation Provider
= KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair()
= KeyPairGenerator.getInstance( “RSA”, "AndroidKeyStore") // Initialize KeyPairGenerator // KeyPairGeneratorSpec - deprecated,available from 18 API // KeyGenParameterSpec - available from 23 + API return generator.generateKeyPair() Generate a Key Pair with initialized Specification
= KeyPairGeneratorSpec.Builder(context) .setAlias(alias) .setSerialNumber(BigInteger.ONE) .setSubject(X500Principal("CN=${alias} CA Certificate")) .setStartDate(startDate.time) .setEndDate(endDate.time) generator.initialize(builder.build()) Builder to get Key Pair Generator Spec instance, deprecated in API 23
= KeyPairGeneratorSpec.Builder(context) .setAlias(alias) .setSerialNumber(BigInteger.ONE) .setSubject(X500Principal("CN=${alias} CA Certificate")) .setStartDate(startDate.time) .setEndDate(endDate.time) generator.initialize(builder.build()) String, unique, identifier of the Key. Saving another key with the same alias will overwrite the first one.
= KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) builder.setBlockModes(KeyProperties.BLOCK_MODE_ECB) builder.setEncryptionPaddings( KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) generator.initialize(builder.build()) Builder to get Key Gen Parameter Spec instance, added in API 23. Used for asymmetric and symmetric keys
= KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) builder.setBlockModes(KeyProperties.BLOCK_MODE_ECB) builder.setEncryptionPaddings( KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) generator.initialize(builder.build()) Purpose of Key usage must be specified
= KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) builder.setBlockModes(KeyProperties.BLOCK_MODE_ECB) builder.setEncryptionPaddings( KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) generator.initialize(builder.build()) As well as Cipher Block Mode
keyStore.getKey(alias, null) as PrivateKey? val publicKey = keyStore.getCertificate(alias)?.publicKey return if (privateKey != null && publicKey != null) { KeyPair(publicKey, privateKey) } else { null } Get a key by alias
keyStore.getKey(alias, null) as PrivateKey? val publicKey = keyStore.getCertificate(alias)?.publicKey return if (privateKey != null && publicKey != null) { KeyPair(publicKey, privateKey) } else { null } Get a certificate by alias
keyStore.getKey(alias, null) as PrivateKey? val publicKey = keyStore.getCertificate(alias)?.publicKey return if (privateKey != null && publicKey != null) { KeyPair(publicKey, privateKey) } else { null } Get a certificate by alias
"RSA/ECB/PKCS1Padding" val cipher: Cipher = Cipher.getInstance(transformation) fun encrypt(data: String, key: Key?): String { cipher.init(Cipher.ENCRYPT_MODE, key) val bytes = cipher.doFinal(data.toByteArray()) return Base64.encodeToString(bytes, Base64.DEFAULT) } Factory method to get Cipher instance for given transformation
"RSA/ECB/PKCS1Padding" val cipher: Cipher = Cipher.getInstance(transformation) fun encrypt(data: String, key: Key?): String { cipher.init(Cipher.ENCRYPT_MODE, key) val bytes = cipher.doFinal(data.toByteArray()) return Base64.encodeToString(bytes, Base64.DEFAULT) } Make actual encryption of byte array
key: Key?): String { cipher.init(Cipher.DECRYPT_MODE, key) val encryptedData = Base64.decode(data, Base64.DEFAULT) val decodedData = cipher.doFinal(encryptedData) return String(decodedData) } Decode encrypted text to the byte array
key: Key?): String { cipher.init(Cipher.DECRYPT_MODE, key) val encryptedData = Base64.decode(data, Base64.DEFAULT) val decodedData = cipher.doFinal(encryptedData) return String(decodedData) } Make actual decryption of byte array
small amount of data Valid message length depends on the key size The bigger key is, the bigger message can be encrypted Separate data and encrypt it part by part Use symmetric key to encrypt data and encrypt it with RSA key #dfua
generateDefaultSymmetricKey(): SecretKey { val keyGenerator = KeyGenerator.getInstance("AES") return keyGenerator.generateKey() } Factory method to get Key Generator instance with given Algorithm
#dfua val keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") val builder = KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC) builder.setEncryptionPaddings( KeyProperties.ENCRYPTION_PADDING_PKCS7) keyGenerator.init(builder.build()) return keyGenerator.generateKey() Factory method to get Key Generator instance with given Algorithm and Provider
#dfua val keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") val builder = KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC) builder.setEncryptionPaddings( KeyProperties.ENCRYPTION_PADDING_PKCS7) keyGenerator.init(builder.build()) return keyGenerator.generateKey() There is new Key Properties class in API 23, that contains various helpful constants.
String, algorithm: String, wrappedKeyType: Int,keyToUnWrapWith: Key?): Key { val data = Base64.decode(wrappedKeyData, Base64.DEFAULT) cipher.init(Cipher.UNWRAP_MODE, keyToUnWrapWith) return cipher.unwrap(data, algorithm, wrappedKeyType) } For unwrapping we need to explicitly specify algorithm that encrypted key was created with
val cipher = CipherWrapper( CipherWrapper.TRANSFORMATION_ASYMMETRIC) val symmetricKey = cipher.unWrapKey( encryptionKey, ALGORITHM_AES, Cipher.SECRET_KEY, masterKey?.private) as SecretKey
Is typically required to be random or pseudorandom Randomization is crucial to achieve semantic security The point of an IV is to tolerate the use of the same key to encrypt several distinct messages. #dfua
key) var result = "" if (useInitializationVector) { val iv = cipher.iv val ivString = Base64.encodeToString(iv, Base64.DEFAULT) result = ivString + IV_SEPARATOR } val bytes = cipher.doFinal(data.toByteArray()) result += Base64.encodeToString(bytes, Base64.DEFAULT) return result
key) var result = "" if (useInitializationVector) { val iv = cipher.iv val ivString = Base64.encodeToString(iv, Base64.DEFAULT) result = ivString + IV_SEPARATOR } val bytes = cipher.doFinal(data.toByteArray()) result += Base64.encodeToString(bytes, Base64.DEFAULT) return result Automatically Generated Initialization Vector
key) var result = "" if (useInitializationVector) { val iv = cipher.iv val ivString = Base64.encodeToString(iv, Base64.DEFAULT) result = ivString + IV_SEPARATOR } val bytes = cipher.doFinal(data.toByteArray()) result += Base64.encodeToString(bytes, Base64.DEFAULT) return result We will add as prefix to encryption result, for later usage
key) var result = "" if (useInitializationVector) { val iv = cipher.iv val ivString = Base64.encodeToString(iv, Base64.DEFAULT) result = ivString + IV_SEPARATOR } val bytes = cipher.doFinal(data.toByteArray()) result += Base64.encodeToString(bytes, Base64.DEFAULT) return result Add encrypted string as a postfix to the result
init Cipher #dfua val split = data.split(IV_SEPARATOR.toRegex()) if (split.size != 2) throw IllegalArgumentException("Passed data is incorrect. There was no IV specified with it.") val ivString = split[0] encodedString = split[1] val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT)) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec)
init Cipher #dfua val split = data.split(IV_SEPARATOR.toRegex()) if (split.size != 2) throw IllegalArgumentException("Passed data is incorrect. There was no IV specified with it.") val ivString = split[0] encodedString = split[1] val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT)) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) Split encrypted data by our predefined Initialization Vector separator
init Cipher #dfua val split = data.split(IV_SEPARATOR.toRegex()) if (split.size != 2) throw IllegalArgumentException("Passed data is incorrect. There was no IV specified with it.") val ivString = split[0] encodedString = split[1] val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT)) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) Get Initialization vector
init Cipher #dfua val split = data.split(IV_SEPARATOR.toRegex()) if (split.size != 2) throw IllegalArgumentException("Passed data is incorrect. There was no IV specified with it.") val ivString = split[0] encodedString = split[1] val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT)) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) Get encrypted text
init Cipher #dfua val split = data.split(IV_SEPARATOR.toRegex()) if (split.size != 2) throw IllegalArgumentException("Passed data is incorrect. There was no IV specified with it.") val ivString = split[0] encodedString = split[1] val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT)) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) Create Initialization Vector Parameter Specification
init Cipher #dfua val split = data.split(IV_SEPARATOR.toRegex()) if (split.size != 2) throw IllegalArgumentException("Passed data is incorrect. There was no IV specified with it.") val ivString = split[0] encodedString = split[1] val ivSpec = IvParameterSpec(Base64.decode(ivString, Base64.DEFAULT)) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) And initialize cipher for decryption mode with it
with the secure lock screen credential (e.g., password, PIN, or pattern). Note that this feature requires that the secure lock screen (e.g., password, PIN, pattern) is set up, otherwise key pair generation will fail. Moreover, this key pair will be deleted when the secure lock screen is disabled or reset (e.g., by the user or a Device Administrator).” #dfua
safely on M devices and later Before M, reload data when keys are invalidated Do not use Android Key Store for local only content Instead prefer to use default java Provider (or other) #dfua
File( context.filesDir, defaultKeyStoreName) val store = KeyStore.getInstance(KeyStore.getDefaultType()) if (!defaultKeyStoreFile.exists()) { store.load(null) } else { store.load(FileInputStream(defaultKeyStoreFile), null) } return store
File( context.filesDir, defaultKeyStoreName) val store = KeyStore.getInstance(KeyStore.getDefaultType()) if (!defaultKeyStoreFile.exists()) { store.load(null) } else { store.load(FileInputStream(defaultKeyStoreFile), null) } return store Create a file for the key store
File( context.filesDir, defaultKeyStoreName) val store = KeyStore.getInstance(KeyStore.getDefaultType()) if (!defaultKeyStoreFile.exists()) { store.load(null) } else { store.load(FileInputStream(defaultKeyStoreFile), null) } return store Initialize empty keystore if file not exists yet
File( context.filesDir, defaultKeyStoreName) val store = KeyStore.getInstance(KeyStore.getDefaultType()) if (!defaultKeyStoreFile.exists()) { store.load(null) } else { store.load(FileInputStream(defaultKeyStoreFile), null) } return store Load keystore from file otherwise
#dfua val key = generateDefaultSymmetricKey() val keyEntry = KeyStore.SecretKeyEntry(key) defaultKeyStore.setEntry(alias, keyEntry, KeyStore.PasswordProtection(password.toCharArray())) defaultKeyStore.store(FileOutputStream(defaultKeyStoreFile), password.toCharArray()) Generate default symmetric key, as we already did at level 2
#dfua val key = generateDefaultSymmetricKey() val keyEntry = KeyStore.SecretKeyEntry(key) defaultKeyStore.setEntry(alias, keyEntry, KeyStore.PasswordProtection(password.toCharArray())) defaultKeyStore.store(FileOutputStream(defaultKeyStoreFile), password.toCharArray()) Create a Secret Entry for this key
#dfua val key = generateDefaultSymmetricKey() val keyEntry = KeyStore.SecretKeyEntry(key) defaultKeyStore.setEntry(alias, keyEntry, KeyStore.PasswordProtection(password.toCharArray())) defaultKeyStore.store(FileOutputStream(defaultKeyStoreFile), password.toCharArray()) Set this entry to keystore, with alias and password
#dfua val key = generateDefaultSymmetricKey() val keyEntry = KeyStore.SecretKeyEntry(key) defaultKeyStore.setEntry(alias, keyEntry, KeyStore.PasswordProtection(password.toCharArray())) defaultKeyStore.store(FileOutputStream(defaultKeyStoreFile), password.toCharArray()) And finally update the file with keystore entries
getDefaultKeyStoreSymmetricKey(alias: String, keyPassword: String): SecretKey? { return try { defaultKeyStore.getKey(alias, keyPassword.toCharArray()) as SecretKey } catch (e: UnrecoverableKeyException) { null } } We need to provide password to get a key
getDefaultKeyStoreSymmetricKey(alias: String, keyPassword: String): SecretKey? { return try { defaultKeyStore.getKey(alias, keyPassword.toCharArray()) as SecretKey } catch (e: UnrecoverableKeyException) { null } } Exception will be thrown if key with given alias and password doesn’t exist in keystore
Tied to AndroidKeyStore and requires to create Fingerprint Key Keys gets invalidated when fingerprint is added or removed On AVDs keys may be never invalidated (API 24) This is also valid for devices (Samsung s6) #dfua
= fingerprintManager?.isHardwareDetected ?: false fun hasEnrolledFingerprints(): Boolean = fingerprintManager?.hasEnrolledFingerprints() ?: false Fingerprint Manager SystemServices class #dfua System service that is responsible for fingerprint. Available from API 23. There is FingerprintManagerCompat in support library, that behaves like there is no fingerprint hardware, on devices running below 23 API (not using it as it is not working with AVD 23 API and on AVD 24+ API keys are not invalidating)
= fingerprintManager?.isHardwareDetected ?: false fun hasEnrolledFingerprints(): Boolean = fingerprintManager?.hasEnrolledFingerprints() ?: false Fingerprint Manager SystemServices class #dfua Check if there is fingerprint hardware on device
= fingerprintManager?.isHardwareDetected ?: false fun hasEnrolledFingerprints(): Boolean = fingerprintManager?.hasEnrolledFingerprints() ?: false Fingerprint Manager SystemServices class #dfua Returns true if there was at least one fingerprint added. Is not working properly after device reboot on AVD 23, returns false even if there were fingerprints added. Becomes to work properly after adding / removing fingerprint.
handler: Handler?) { fingerprintManager?.authenticate( cryptoObject,cancellationSignal,flags,callback,handler) } Fingerprint Manager SystemServices class #dfua Holder for Cipher, that will be authenticated with fingerprint
handler: Handler?) { fingerprintManager?.authenticate( cryptoObject,cancellationSignal,flags,callback,handler) } Fingerprint Manager SystemServices class #dfua From Docs: “optional flags; should be 0”
handler: Handler?) { fingerprintManager?.authenticate( cryptoObject,cancellationSignal,flags,callback,handler) } Fingerprint Manager SystemServices class #dfua Our main point of interest here, will be called during authentication
handler: Handler?) { fingerprintManager?.authenticate( cryptoObject,cancellationSignal,flags,callback,handler) } Fingerprint Manager SystemServices class #dfua Warms up the fingerprint hardware and starts scanning for a fingerprint
fun onAuthenticationFailed() fun onAuthenticationSucceeded(result: AuthenticationResult) Fingerprint AuthenticationCallback AuthenticationFingerprint class #dfua
KeyGenParameterSpec.Builder = ... builder.setUserAuthenticationRequired(true) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { builder.setInvalidatedByBiometricEnrollment(true) } Requers created key to be authenticated with fingerprint
val symmetricKey = keyStoreWrapper .getAndroidKeyStoreSymmetricKey(FINGERPRINT_KEY) val cipher = CipherWrapper( CipherWrapper.TRANSFORMATION_SYMMETRIC).cipher cipher.init(Cipher.ENCRYPT_MODE, symmetricKey) FingerprintManager.CryptoObject(cipher) } Simple wrapper for Cipher that is passed to FingerprintManager
Throwable) { if (e is Key Permanently Invalidated Exception || e is IllegalBlockSizeException) { return null } else if (e is InvalidKeyException) { return null } throw e }
Throwable) { if (e is Key Permanently Invalidated Exception || e is IllegalBlockSizeException) { return null } else if (e is InvalidKeyException) { return null } throw e } Fingerprint Key was invalidated
Throwable) { if (e is Key Permanently Invalidated Exception || e is IllegalBlockSizeException) { return null } else if (e is InvalidKeyException) { return null } throw e } Fingerprint Key was not generated
cryptoObject.cipher.doFinal(KEY_VALIDATION_DATA) return true } catch (e: Throwable) { if (e is KeyPermanentlyInvalidatedException || e is IllegalBlockSizeException) { return false } throw e }
cryptoObject.cipher.doFinal(KEY_VALIDATION_DATA) return true } catch (e: Throwable) { if (e is KeyPermanentlyInvalidatedException || e is IllegalBlockSizeException) { return false } throw e } Run final authentication test
cryptoObject.cipher.doFinal(KEY_VALIDATION_DATA) return true } catch (e: Throwable) { if (e is KeyPermanentlyInvalidatedException || e is IllegalBlockSizeException) { return false } throw e } Will be thrown if key was invalidated after during authentication
KeyGenParameterSpec.Builder = ... builder.setUserAuthenticationRequired(true) builder.setUserAuthenticationValidityDurationSeconds(delay) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { builder.setUserAuthenticationValidWhileOnBody(true) } Marks this key to require confirm credentials validation
KeyGenParameterSpec.Builder = ... builder.setUserAuthenticationRequired(true) builder.setUserAuthenticationValidityDurationSeconds(delay) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { builder.setUserAuthenticationValidWhileOnBody(true) } Sets whether the key will remain authorized only until the device is removed from the user's body
Throwable) { if (e is UserNotAuthenticatedException || e is KeyPermanentlyInvalidatedException) { return false } else if (e is InvalidKeyException) { return false } throw e } #dfua
Throwable) { if (e is UserNotAuthenticatedException || e is KeyPermanentlyInvalidatedException) { return false } else if (e is InvalidKeyException) { return false } throw e } #dfua User is not authenticated or the lock screen has been disabled or reset
Throwable) { if (e is UserNotAuthenticatedException || e is KeyPermanentlyInvalidatedException) { return false } else if (e is InvalidKeyException) { return false } throw e } #dfua Confirm Credentials key was not generated
as KeyguardManager val intent = keyguard.createConfirmDeviceCredentialIntent( title, description) if (intent != null) { activity.startActivityForResult(intent, requestCode) } #dfua Confirm Credentials is a part of KeyguardManager system service
as KeyguardManager val intent = keyguard.createConfirmDeviceCredentialIntent( title, description) if (intent != null) { activity.startActivityForResult(intent, requestCode) } #dfua Creates intent for launching the activity or null if no password is required(no Lock Screen setup). It is available from API 21+ and can be used without cryptographic keys (but it will be not possible to specify user authentication validity duration seconds without it)
as KeyguardManager val intent = keyguard.createConfirmDeviceCredentialIntent( title, description) if (intent != null) { activity.startActivityForResult(intent, requestCode) } #dfua You can customize the title and description. Or a generic one will be provided for you if you leave it empty
as KeyguardManager val intent = keyguard.createConfirmDeviceCredentialIntent( title, description) if (intent != null) { activity.startActivityForResult(intent, requestCode) } #dfua Start activity with confirm credentials intent and wait for RESULT_OK
not all of them works as designed to Do not use Android Key Store API on pre M devices Or use it if you not scared to lose data (can be reloaded) Choose the Key Algorithm that is best for your needs Remember that asymmetric Keys are not good for large data #dfua
Choose hardware-backed keys when possible Fingerprint is not the main security option Always handle cases of key invalidation Remember that there is a fingerprint compatibility helper Use Confirm Credentials instead of custom screen locks #dfua