版权归作者所有,转发请注明出处:https://www.jianshu.com/p/19ab513dcb70
Android Google源生生物识别(FingerprintManager)
Android Google源生生物识别(Biometric依赖库)
前言
Google从Android6.0(API 23)开始提供了指纹识别的官方支持,然后从9.0开始对其进行扩展更改为更强大易用的生物识别支持,本API是基于API 23的FingerprintManager集成指纹识别
1.检查应用是否可以使用指纹识别
由于并不是所有的情况下都可以在APP中引入指纹识别功能,首先你需要硬件拥有指纹识别传感器,并且有在系统设置中录入至少一个指纹信息以及其他的一些前置必须条件才可以正常的在APP中使用指纹识别功能
- 系统版本检查
由于指纹识别功能是从API23开始支持,所以首先需要判断当前设备系统版本是否支持 - 是否已经在手机系统中录入一个指纹信息
只有在设备系统中设置过一个指纹信息,才可以在APP中正常的使用指纹功能 - 设备是否支持使用指纹识别
用来检测设备指纹传感器是否可以正常使用 - 设备是否已经拥有安全设置
检测设备是否已经设置过安全密码(锁屏密码,锁屏图案等) - 是否已经添加使用指纹权限
如果未添加指纹权限则在调指纹传感器时会提示权限缺失
fun checkFingerprintAvailable(context: Context) =
when{
Build.VERSION.SDK_INT < Build.VERSION_CODES.M ->{
false
}
ContextCompat.checkSelfPermission(context,Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_DENIED ->{
false
}
(context.getSystemService(Context.KEYGUARD_SERVICE) as? KeyguardManager)?.isDeviceSecure!= true ->{
false
}
FingerprintManagerCompat.from(context).run { !(isHardwareDetected&&hasEnrolledFingerprints()) } ->{
false
}
else ->{
true
}
}
2.创建需要用户授权的密钥
当我们调用指纹传感器时需要生成一个需要用户授权才可以使用的密钥,然后当用户调用指纹传感器识别指纹成功,相当于通过了用户授权然后则可以使用对应的密钥对登录信息进行加密,保存加密信息到内存中,当下次登录时直接可以使用密钥解密密文拿到用户信息自动调用登录Api,从而方便用户使用,要完成以上步骤我们需要完成下列功能
- 创建需要用户授权的密钥
@Throws(
NoSuchProviderException::class,
NoSuchAlgorithmException::class,
InvalidAlgorithmParameterException::class
)
fun createKEY(): SecretKey? {
val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore"
)
val build = KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or
KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setKeySize(256)
.build()
keyGenerator.init(build)
return keyGenerator.generateKey()
}
- 提供加密/解密用户信息的方法
@Throws(Exception::class)
fun encyption( plaintext: String, cipher: Cipher): CipherTextWrapper {
val encodedBytes = cipher.doFinal(plaintext.toByteArray(Charset.forName("UTF-8")))
return CipherTextWrapper(encodedBytes,cipher.iv)
}
@Throws(java.lang.Exception::class)
fun decyption(encrypted: ByteArray?,cipher: Cipher): String {
return String(cipher.doFinal(encrypted), Charset.forName("UTF-8"))
}
- 提供存储/读取加密用户信息的方法
CipherTextWrapper是封装用来存储加密信息的实体类,由于需要解密所以我们同时存储了IV
data class CipherTextWrapper(val cipherText: ByteArray, val iv: ByteArray)
fun saveCipherTextToSharedPreference(context: Context,cipherText:CipherTextWrapper){
with(context.getSharedPreferences(SHARED_PREFERENCE_MIKE,Context.MODE_PRIVATE)){
edit().putString(SHARED_PREFERENCE_MIKE_KEY,Gson().toJson(cipherText)).apply()
}
}
fun getCipherTextFromSharedPreference(context: Context):CipherTextWrapper{
context.getSharedPreferences(SHARED_PREFERENCE_MIKE,Context.MODE_PRIVATE).getString(SHARED_PREFERENCE_MIKE_KEY,null).let {
return Gson().fromJson(it,CipherTextWrapper::class.java)
}
}
- 提供获取Cipher对象的方法
当你需要对信息加密/解密时需要对Cipher进行对应的初始化,根据业务初始化不同模式的Cipher
fun getCipher(context: Context): Cipher {
val cipher = Cipher.getInstance(AES_MODE_GCM)
if (getBindFingerprintStatus(context)) {
cipher.init(
KeyProperties.PURPOSE_DECRYPT,
createKEY(),
GCMParameterSpec(128,getCipherTextFromSharedPreference(context).iv)
)
} else {
cipher.init(KeyProperties.PURPOSE_ENCRYPT, createKEY())
}
return cipher
}
3.调用指纹识别传感器进行指纹识别
需要调用FingerprintManager的authenticate函数调用指纹识别传感器
@param crypto object associated with the call or null if none required.
加密对象:创建对应的Cipher对象并对其进行初始化,此密钥只有在用户通过指纹识别之后才可以进行使用,Cipher对象的工作模式根据业务变化
- 如果你需要在用户指纹识别成功也就是用户授权之后加密用户登录信息则需要初始化加密模式的Cipher对象,在指纹校验成功之后加密用户信息然后对其进行存储,这个过程也可以叫做指纹注册
- 如果你需要在用户指纹识别成功也就是用户授权之后解密用户信息然后使用此信息完成用户的登录操作,则需要初始化解密模式的Cipher对象,这个过程就是指纹登录,表现上你只需要识别指纹就可以达到登录的目的
@param cancel an object that can be used to cancel authentication
根据业务需求可以调用其cancel()函数,用来控制结束此指纹识别
@param callback an object to receive authentication events
用来获取指纹识别的结果
if (FingerprintHelper.checkFingerprintAvailable(this)) {
(getSystemService(Context.FINGERPRINT_SERVICE) as? FingerprintManager)?.let {
it.authenticate(
FingerprintManager.CryptoObject(
FingerprintCryptographyManager.getCipher(this)), cancellationSignal, 0, MikeFingerprintAuthCallback(this), null
)
}
}
4.设置指纹并使用密钥加密用户信息
在指纹识别成功之后我们可以获取到经过授权的密钥,然后使用此密钥加密用户信息并且进行存储
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
result?.cryptoObject?.cipher?.let {
FingerprintCryptographyManager.saveCipherTextToSharedPreference(context,FingerprintCryptographyManager.encyption("abc:123",it))
FingerprintCryptographyManager.setBindFingerPrintStatus(context,true)
}
}
5.使用指纹登录并解密用户信息
当你之前已经识别过指纹并且将用户信息加密存储之后,后续的登录操作就可以使用直接使用指纹自动去解密信息并且登录
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
super.onAuthenticationSucceeded(result)
result?.cryptoObject?.cipher?.let {
val cipherTextFromSharedPreference = FingerprintCryptographyManager.getCipherTextFromSharedPreference(context)
//TODO use info to login
}
}
6.指纹识别提示UI
基于API23没有系统自带的指纹识别弹框,当我们调用指纹传感器时需要提供指纹UI去提示用户,从而让用户知道指纹传感器已经开始工作从而将手指放入传感器之上,Biomteric依赖库将会库提供自带的UI或者系统UI Android Google源生生物识别(API28)
- 指纹提示UI需要和指纹传感器的状态保持一致,当弹框消失时指纹传感器也应该停止工作,当指纹验证失败时弹框的状态也应该随之变化
- 需要注意不同设备的适配情况,特别是一些厂商的定制系统已经自带了弹框,或者屏上指纹自带弹框的情况
代码链接:
https://github.com/huangyiCode/android_google_fingerprint
6.其他
指纹信息变更的安全问题
指纹识别的错误次数
不同设备的适配问题
考虑用密钥加密何种信息
欢迎关注Mike的
Android 知识整理