前言
Google在Android6.0
开始已经有官方API支持指纹识别,每次讨论这个话题总绕不开国产五彩缤纷的ROM,国产部分手机在Android6.0
之前有已经实现了自己的指纹识别功能,具体的适配需要去查阅厂商开发文档,本文不深入讨论。
想要实现指纹识别在AndroidM(6.0)~ Android P(9.0)
有 FingerprintManager,而在Android P(9.0)之后
,Google推荐使用
BiometricPrompt来实现生物识别认证,当然这个生物识别不止是指纹识别,可能会有虹膜、人脸识别等,本文记录Android6.0~Android9.0
指纹识别以及Android9.0
以上的指纹识别处理。
Android6.0。
Android6.0实现指纹识别需要用到的核心类为FingerprintManager,为了更好的兼容及完善API,Google推荐使用的是FingerprintManagerCompat。
需要的权限:
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
1
2
3
private val fingerprint by lazy {
FingerprintManagerCompat.from(context)
}
在开始指纹识别之前,需要判断硬件是否支持,Google提供的检测方法为:
1
context.packageManager.hasSystemFeature(PackageManager#FEATURE_FINGERPRINT)
也可以使用
1
FingerprintManagerCompat.from(context).isHardwareDetected
确定了设备支持指纹识别之后,方可调用认证方法
1
FingerprintManagerCompat#authenticate(crypto: FingerprintManager.CryptoObject?, cancel: CancellationSignal?, flags: Int, callback: FingerprintManager.AuthenticationCallback, handler: Handler?)
调用该方法之后设备的指纹识别模块会进入监听状态,在认证失败、成功、取消或者超时之后才会停止监听,在这期间无法再次调用指纹识别。
参数解释:
参数 | 解释 |
---|---|
crypto:CryptoObject | FingerprintManager提供的数据加密包装类,支持Signature、Cipher。 |
cancel:CancellationSignal | 用于监听指纹识别的取消操作 |
flags | 填0即可 |
callback:AuthenticationCallback | 指纹识别回调 |
handler:Handler | 用于接收回调事件的Handler,可以为null。 |
需要重点了解一下CryptoObject,这个提供了指纹数据的加密及解密。
该包装类目前支持的加密对象分别为
指纹信息保存需要用到AES加密,创建一个Cipher,系统提供的方法为Cipher.getInstance(transformation)
。
transformation
的格式为:
- “algorithm/mode/padding”
- “algorithm”
如:
1
val mCipher = Cipher.getInstance("<i>DES/CBC/PKCS5Padding</i>")
具体的算法规则参考:Cipher#Algorithm。 所以创建一个Cipher可以这么写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CipherCreator {
companion object {
const val KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
const val TRANSFORMATION = "$KEY_ALGORITHM/$BLOCK_MODE/$ENCRYPTION_PADDING"
}
private fun createCipher(): Cipher {
return try {
Cipher.getInstance(TRANSFORMATION)
} catch (e: NoSuchAlgorithmException) {
throw Exception("Could not create the cipher for fingerprint authentication.", e)
}
}
}
创建好了Cipher需要进行初始化,调用的方法为Cipher#init(opmode: Int, key: Key!)
,api解释为。
1
fun init(opmode: Int, key: Key!): Unit
Initializes this cipher with a key.
The cipher is initialized for one of the following four operations: encryption, decryption, key wrapping or key unwrapping, depending on the value of opmode.
大意为根据opmode
对Cipher进行初始化,一般opmode
对应的操作为:
- 加密
- 解密
- 包装key
- 解包key
另一个参数Key的获取方式为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CipherCreator {
companion object {
const val KEY_NAME = "com.example.github.CipherCreator"
const val KEYSTORE_NAME = "AndroidKeyStore"
const val KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
const val TRANSFORMATION = "$KEY_ALGORITHM/$BLOCK_MODE/$ENCRYPTION_PADDING"
}
private val keystore by lazy {
KeyStore.getInstance(KEYSTORE_NAME).apply {
load(null)
}
}
private fun getKey(): Key {
if (!keystore.isKeyEntry(KEY_NAME)) {
createKey()
}
return keystore.getKey(KEY_NAME, null)
}
private fun createKey() {
val keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME)
val keyGenSpec = KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build()
keyGen.init(keyGenSpec)
keyGen.generateKey()
}
}
所以获取一个Cipher的完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class CipherCreator {
companion object {
const val KEY_NAME = "com.example.github.CipherCreator"
const val KEYSTORE_NAME = "AndroidKeyStore"
const val KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
const val TRANSFORMATION = "$KEY_ALGORITHM/$BLOCK_MODE/$ENCRYPTION_PADDING"
}
private val keystore by lazy {
KeyStore.getInstance(KEYSTORE_NAME).apply {
load(null)
}
}
fun createCipher(): Cipher {
val key = getKey()
return try {
Cipher.getInstance(TRANSFORMATION).apply {
init(Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE, key)
}
} catch (e: Exception) {
throw Exception("Could not create the cipher for fingerprint authentication.", e)
}
}
private fun getKey(): Key {
if (!keystore.isKeyEntry(KEY_NAME)) {
createKey()
}
return keystore.getKey(KEY_NAME, null)
}
private fun createKey() {
val keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME)
val keyGenSpec = KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build()
keyGen.init(keyGenSpec)
keyGen.generateKey()
}
}
指纹验证的所需参数获取完毕,调用指纹验证。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* core
*/
private val fingerprint by lazy {
FingerprintManagerCompat.from(holder)
}
/**
* crypto
*/
private val cryptoObj by lazy {
FingerprintManagerCompat.CryptoObject(CipherCreator().createCipher())
}
/**
* cancelSignal
*/
private val cancellationSignal by lazy {
CancellationSignal()
}
fun authenticate() {
fingerprint.authenticate(cryptoObj, 0, cancellationSignal, object :
FingerprintManagerCompat.AuthenticationCallback() {
override fun onAuthenticationError(errMsgId: Int, errString: CharSequence) {
}
override fun onAuthenticationFailed() {
}
override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence) {
}
override fun onAuthenticationSucceeded(result: FingerprintManagerCompat.AuthenticationResult?) {
}
}, null)
}
Android 9.0
Android9.0
实现指纹识别的核心类为BiometricPrompt,在Android6.0
中指纹识别的对话框需要自己实现,而BiometricPrompt提供了系统的对话框来进行验证。
BiometricPrompt指纹识别的内部实现还是使用了FingerprintManager,不过在其之上做了微小的调整,比如提供系统的Dialog,更加人性化的回调,核心实现还是FingerprintManager。
在Android9.0
判断设备是否支持指纹识别,可以调用BiometricManager#canAuthenticate
该方法回返回一个int。
- BIOMETRIC_SUCCESS
- BIOMETRIC_ERROR_NONE_ENROLLED 没有录入指纹
- BIOMETRIC_ERROR_NO_HARDWARE 硬件不支持
需要的权限为:
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
使用BiometricPrompt来实指纹识别的步骤为:
创建BiometricPrompt
1
2
3
4
5
6
val mBiometricPrompt = BiometricPrompt.Builder(context)
.setTitle("指纹验证")
.setNegativeButton("取消", context.mainExecutor,
DialogInterface.OnClickListener { _, _ ->
}).build()
调用authenticate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mBiometricPrompt.authenticate(
cryptObject,
cancellationSignal,
context.mainExecutor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errMsgId: Int, errString: CharSequence) {
}
override fun onAuthenticationFailed() {
}
override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence) {
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult?) {
}
})
调用认证所需参数跟Android6.0
参数生成一致。