首页

源码搜藏网

首页 > 安卓源码 > 技术博客 >

Android 中的指纹识别

创建时间:2016-11-07 14:34  浏览

最近项目需要使用到指纹识别的功能,查阅了相关资料后,整理成此文。

指纹识别是在Android 6.0之后新增的功能,因此在使用的时候需要先判断用户手机的系统版本是否支持指纹识别。另外,实际开发场景中,使用指纹的主要场景有两种:

由于使用指纹识别功能需要一个加密对象(CryptoObject)该对象一般是由对称加密或者非对称加密获得。上述两种开发场景的实现大同小异,主要区别在于加密过程中密钥的创建和使用,一般来说,纯本地的使用指纹识别功能,只需要对称加密即可;而与后台交互则需要使用非对称加密:将私钥用于本地指纹识别,识别成功后将加密信息传给后台,后台开发人员用公钥解密,以获得用户信息。

下面先简单介绍一下对称加密和非对称加密的相关概念,然后对两种开发方式的实现分别进行讲解。

对称加密、非对称加密和签名

在正式使用指纹识别功能之前,有必要先了解一下对称加密和非对称加密的相关内容。

由以上内容可以了解到,对称加密和非对称加密的特点如下:

指纹识别的对称加密实现

使用指纹识别的对称加密功能的主要流程如下:

创建密钥

创建密钥要涉及到两个类:KeyStore 和 KeyGenerator。

KeyStore 是用于存储、获取密钥(Key)的容器,获取 KeyStore的方法如下:

1
2
3
4
5
try {
    mKeyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException e) {
    throw new RuntimeException("Failed to get an instance of KeyStore", e);
}

而生成 Key,如果是对称加密,就需要 KeyGenerator 类。获取一个 KeyGenerator 对象比较简单,方法如下:

1
2
3
4
5
6
7
// 对称加密, 创建 KeyGenerator 对象
try {
    mKeyGenerator = KeyGenerator
            .getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
    throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
}

获得 KeyGenerator 对象后,就可以生成一个 Key 了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 try {
    keyStore.load(null);
    KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(defaultKeyName,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setUserAuthenticationRequired(true)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        builder.setInvalidatedByBiometricEnrollment(true);
    }
    keyGenerator.init(builder.build());
    keyGenerator.generateKey();
} catch (CertificateException | NoSuchAlgorithmException | IOException | InvalidAlgorithmParameterException e) {
    e.printStackTrace();
}

关于 KeyStrore 和 KeyGenerator 的相关介绍,推荐阅读:Android KeyStore + FingerprintManager 存储密码

创建并初始化 Cipher 对象

Cipher 对象是一个按照一定的加密规则,将数据进行加密后的一个对象。调用指纹识别功能需要使用到这个对象。创建 Cipher 对象很简单,如同下面代码那样:

1
2
3
4
5
6
7
Cipher defaultCipher;
try {
    defaultCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
            + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
    throw new RuntimeException("创建Cipher对象失败", e);
}

然后使用刚才创建好的密钥,初始化 Cipher 对象:

1
2
3
4
5
6
7
8
 try {
    keyStore.load(null);
    SecretKey key = (SecretKey) keyStore.getKey(keyName, null);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return true;
} catch (IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyStoreException | InvalidKeyException e) {
    throw new RuntimeException("初始化 cipher 失败", e);
}

使用指纹识别功能

真正到了使用指纹识别功能的时候,你会发现其实很简单,只是调用 FingerprintManager 类的的方法authenticate()而已,然后系统会有相应的回调反馈给我们,该方法如下:

1
public void authenticate(CryptoObject crypto, CancellationSignal cancel, int flags, AuthenticationCallback callback, Handler handler)

该方法的几个参数解释如下:

完成指纹识别后,还要记得将 AuthenticationCallback 关闭掉:

1
2
3
4
5
6
7
public void stopListening() {
    if (cancellationSignal != null) {
        selfCancelled = true;
        cancellationSignal.cancel();
        cancellationSignal = null;
    }
}

重写回调方法

调用了 authenticate() 方法后,系统就会启动指纹传感器,并开始扫描。这时候根据扫描结果,会通过FingerprintManager.AuthenticationCallback类返回几个回调方法:

1
2
3
4
5
6
// 成功
onAuthenticationSucceeded()
// 失败
onAuthenticationFaile()
// 错误
onAuthenticationError()

一般我们需要重写这几个方法,以实现我们的功能。关于onAuthenticationFaile()onAuthenticationError()的区别,后面会讲到。

指纹识别的非对称加密实现

其实流程和上面的流程差不多:

可以看见,指纹识别的非对称加密方式和对称加密方式的实现流程是差不多的,它们之间最明显的差别是在于密钥的生成与使用。

创建密钥

这里要使用 KeyPairGenerator 来创建一组非对称密钥,首先是获取 KeyPairGenerator对象:

1
2
3
4
5
6
// 非对称加密,创建 KeyPairGenerator 对象
try {
    mKeyPairGenerator =  KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
    throw new RuntimeException("Failed to get an instance of KeyPairGenerator", e);
}

得到了 KeyPairGenerator 对象后,就可以创建 KeyPair(密钥对)了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
    // Set the alias of the entry in Android KeyStore where the key will appear
    // and the constrains (purposes) in the constructor of the Builder
    mKeyPairGenerator.initialize(
            new KeyGenParameterSpec.Builder(KEY_NAME,
                    KeyProperties.PURPOSE_SIGN)
                    .setDigests(KeyProperties.DIGEST_SHA256)
                    .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
                    // Require the user to authenticate with a fingerprint to authorize
                    // every use of the private key
                    .setUserAuthenticationRequired(true)
                    .build());
    mKeyPairGenerator.generateKeyPair();
} catch (InvalidAlgorithmParameterException e) {
    throw new RuntimeException(e);
}

签名

指纹识别的对称加密实现中使用了Cipher对象来创建CryptoObject对象,而在这里,我们将会使用私钥进行签名,用签名对象来创建CryptoObject对象:

1
2
3
4
5
6
7
8
9
10
11
12
// 使用私钥签名
try {
    mKeyStore.load(null);
    PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
    mSignature.initSign(key);
    return true;
} catch (KeyPermanentlyInvalidatedException e) {
    return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
        | NoSuchAlgorithmException | InvalidKeyException e) {
    throw new RuntimeException("Failed to init Cipher", e);
}

同样的,调用new FingerprintManager.CryptoObject(mSignature)方法创建一个CryptoObject对象。

调用指纹识别方法

这里的使用方法和前面“指纹识别的对称加密实现”中的调用方法是一样的,都是调用FingerprintManager.authenticate()方法。这里就不再叙述。

监听回调

监听回调也和之前的类似,唯一不同的是,我们在识别成功后需要和后台进行交互,也就是onAuthenticationSucceeded()中处理的逻辑不一样。

实际应用中的注意事项

判断用户是否可以使用指纹识别功能

一般来说,为了增加安全性,要求用户在手机的“设置”中开启了密码锁屏功能。当然,使用指纹解锁的前提是至少录入了一个指纹。

1
2
3
4
5
6
7
8
9
10
// 如果没有设置密码锁屏,则不能使用指纹识别
if (!keyguardManager.isKeyguardSecure()) {
    Toast.makeText(this, "请在设置界面开启密码锁屏功能",
            Toast.LENGTH_LONG).show();
}
// 如果没有录入指纹,则不能使用指纹识别
if (!fingerprintManager.hasEnrolledFingerprints()) {
    Toast.makeText(this, "您还没有录入指纹, 请在设置界面录入至少一个指纹",
            Toast.LENGTH_LONG).show();
}

这里用到了两个类:KeyguardManager 和 FingerprintManager,前者是屏幕保护的相关类。后者是指纹识别的核心类。

关于指纹识别回调方法

前面说到AuthenticationCallback类里面的几个回调方法,其中有三个是我们开发中需要用到的:

1
2
3
onAuthenticationError()
onAuthenticationSucceeded()
onAuthenticationFailed()

关于这三个回调方法,有几点需要注意的:

示例代码

最后, Android Sample 里面关于指纹的示例代码地址如下:

对称加密方式:android-FingerprintDialog。

非对称加密方式:android-AsymmetricFingerprintDialog

上一篇:Android 中Webview 与h5的交互
下一篇:Android之JSON格式数据解析

相关内容

热门推荐