Android指纹识别简记

Android指纹识别简记。

Posted by XYH on September 24, 2019

前言

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。

image


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提供的数据加密包装类,支持SignatureCipher
cancel:CancellationSignal 用于监听指纹识别的取消操作
flags 填0即可
callback:AuthenticationCallback 指纹识别回调
handler:Handler 用于接收回调事件的Handler,可以为null。

需要重点了解一下CryptoObject,这个提供了指纹数据的加密及解密。

该包装类目前支持的加密对象分别为

  • Signature一般用于提供数字签名,保证数据的完整性。
  • Cipher主要用于加密,如进行AES/DES/RSA等。

指纹信息保存需要用到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.

大意为根据opmodeCipher进行初始化,一般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

image


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参数生成一致。

本文Demo地址。