指纹数据是在手机的设置里面,不是存到自己写的APP的。其次,指纹识别就只能识别,而不能在APP中录入指纹,想录入指纹可以,自己要到手机设置里面的指纹功能自己去添加,指纹识别功能能做的就是把用户放到感应区的指纹数据与手机设置里面的已录入的指纹数据进行比对,再执行成功失败的回调,仅此而已
指纹识别是在Android 6.0之后新增的功能,因此在使用的时候需要先判断用户手机的系统版本是否支持指纹识别。另外,实际开发场景中,使用指纹的主要场景有两种:
- 纯本地使用。即用户在本地完成指纹识别后,不需要将指纹的相关信息给后台。
- 与后台交互。用户在本地完成指纹识别后,需要将指纹相关的信息传给后台。
由于使用指纹识别功能需要一个加密对象(CryptoObject)该对象一般是由对称加密或者非对称加密获得。
上述两种开发场景的实现大同小异,主要区别在于加密过程中密钥的创建和使用,一般来说:
- 纯本地的使用指纹识别功能,只需要对称加密即可;
- 后台交互则需要使用非对称加密:将私钥用于本地指纹识别,识别成功后将加密信息传给后台,后台开发人员用公钥解密,以获得用户信息。
指纹识别 API 的版本演进
在 Android 6.0(Android M Api23),Android 系统开放了指纹识别的api,存在于 android.hardware.fingerprint包下,核心类是FingerprintManager,提供了基础的指纹识别的功能。要注意的是,FingerprintManager在 Android 9.0(Android P Api28)做了 @Deprecated 标记,将被弃用。
后来,在android.support.v4.hardware.fingerprint
包和 androidx.core.hardware.fingerprint
包中,FingerprintManager升级为了 FingerprintManagerCompat
,对功能进行了增强,也做了一些兼容性的处理,比如增加了系统版本号的判断,对指纹支持加密处理等。实际上阅读源码会发现,他的核心功能还是调用 FingerprintManager 实现的。
再之后,在 Android 9.0(Android P Api 28),Google 对生物识别进行了进一步增强,开放了以 BiometricPrompt 为核心的新 Api,存在于 androidx.biometric
包和android.hardware.biometrics
包下,Google 在开发者文档中是这样解释的:
On devices running P and above, this will show a system-provided authentication prompt, using a device's supported biometric (fingerprint, iris, face, etc).
大意是,在 Android P 及以上版本的系统中,BiometricPrompt 将展现一个由系统提供的验证提示,用于支持设备提供的生物识别,包括指纹、虹膜、面部等。
目前来看,虹膜和面部等生物识别 Api 尚未开放,仅支持指纹识别,不过在指纹识别上进行了统一,比如要求使用统一的指纹识别 UI ,不允许开发者自定义了。
android9.0问题
(1) Android 9.0 不允许开发者自定义指纹识别框,但系统提供的指纹识别框的灵活性堪忧。比如说,目前来看,系统只允许在识别框出现一个按钮,放了 “取消” 就不能放 “密码验证” ,放了 “密码验证” 就不能放 “取消”。
(2) 系统提供的指纹识别框只能在界面底部,不可以上下居中。但在某些手机上(如OPPO reno),指纹传感器也是在界面底部,当拉起指纹识别时,会在指纹传感器的位置显示一个指纹图标,以提示用户在哪下指。然而,系统提供的指纹识别框上也有一个指纹图标,这两个指纹图标就发生了重合或者离的很近。
解释:
(1)TAM中心是用于存储密钥的服务;
(2)TEE是手机中的独立环境,目前没有有效的破解方法;
(3)APP就是客户端;
(4)后台应用便是后台服务器;
(5)整个验证流程属于三层加密,分别是ATTK,ASK,AUTHKEY;它们分别都有私钥pri和公钥pub,加密时通常用上层私钥加密下层公钥生成密串,然后用下层私钥进行解密。
指纹登录
Google原生API:https://developer.android.com/reference/android/hardware/fingerprint/package-summary.html
阿里指纹2.0:http://open.yunos.com/doc/detail?documentId=102937#
微信soter:https://github.com/Tencent/soter
Google原生API
使用简单不安全。
1.指纹主要返回的就是true和false:
只要你的手机录入过指纹,那么就可以验证指纹通过。那么,假如手机的锁屏密码被知道了,被熊孩子不小心看到,然后他偷偷录入了自己的指纹。最后,他就可以通过这个指纹进行各种验证。换句话说,非常安全的生物指纹,直接降低到了锁屏密码的安全级别;
2.密钥对被劫持:
goolge允许用户在应用中生成一对非对称密钥,将私钥存储在TEE中。但是,如果在密钥生成的时候就被拦截并替换成了黑客自己的密钥,那么也会出现问题;
指纹识别核心方法 authenticate
mCancellationSignal = new CancellationSignal();
fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher), mCancellationSignal, 0, new FingerprintManager.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
if (!isSelfCancelled) {
errorMsg.setText(errString);
if (errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
Toast.makeText(mActivity, errString, Toast.LENGTH_SHORT).show();
dismiss();
}
}
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
errorMsg.setText(helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
FingerprintManager.CryptoObject cryptoObject = result.getCryptoObject();
Cipher cipher1 = cryptoObject.getCipher();
try {
byte[] bytes = cipher1.doFinal();
byte[] bytes1 = cipher.doFinal();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
Toast.makeText(mActivity, "指纹认证成功", Toast.LENGTH_SHORT).show();
mActivity.onAuthenticated();
}
@Override
public void onAuthenticationFailed() {
errorMsg.setText("指纹认证失败,请再试一次");
}
}, null);
参数解析
- crypto 这是一个加密类的对象,指纹扫描器会使用这个对象来判断认证结果的合法性。这个对象可以是null,但是这样的话,就意味这app无条件信任认证的结果,虽然从理论上这个过程可能被攻击,数据可以被篡改,这是app在这种情况下必须承担的风险。因此,建议这个参数不要置为null。这个类的实例化有点麻烦,主要使用javax的security接口实现,后面我的demo程序中会给出一个helper类(CryptoObjectHelper.java),这个类封装内部实现的逻辑,开发者可以直接使用我的类简化实例化的过程。
- cancel 这个是CancellationSignal类的一个对象,这个对象用来在指纹识别器扫描用户指纹的是时候取消当前的扫描操作,如果不取消的话,那么指纹扫描器会移植扫描直到超时(一般为30s,取决于具体的厂商实现),这样的话就会比较耗电。建议这个参数不要置为null。
- flags 标识位,根据上图的文档描述,这个位暂时应该为0,这个标志位应该是保留将来使用的。
- callback这个是FingerprintManager.AuthenticationCallback类的对象,这个是这个接口中除了第一个参数之外最重要的参数了,稍后我们详细来介绍。这个参数不能为NULL。
- handler 这是Handler类的对象,如果这个参数不为null的话,那么FingerprintManager将会使用这个handler中的looper来处理来自指纹识别硬件的消息。通常来讲,开发这不用提供这个参数,可以直接置为null,因为FingerprintManager会默认使用app的main looper来处理。
接口回调
- OnAuthenticationError(int errorCode, ICharSequence errString) 这个接口会再系统指纹认证出现不可恢复的错误的时候才会调用,并且参数errorCode就给出了错误码,标识了错误的原因。
在AndroidP以前,这个方法回调回来之后,指纹识别sensor将会被关闭,也就是说,你再把手指放在指纹硬件上,将不会有反应了。这时候你需要提示用户关闭指纹识别弹窗,或改用密码支付等等
什么情况下会回调error错误呢?比如,连续识别错误5次指纹、指纹硬件不可用等等。 - OnAuthenticationFailed()这个接口会在系统指纹认证失败的情况的下才会回调。注意这里的认证失败和上面的认证错误是不一样的,虽然结果都是不能认证。认证失败是指所有的信息都采集完整,并且没有任何异常,但是这个指纹和之前注册的指纹是不相符的;但是认证错误是指在采集或者认证的过程中出现了错误,比如指纹传感器工作异常等。也就是说认证失败是一个可以预期的正常情况,而认证错误是不可预期的异常情况。
- OnAuthenticationHelp(int helpMsgId, ICharSequence helpString)上面的认证失败是认证过程中的一个异常情况,我们说那种情况是因为出现了不可恢复的错误,而我们这里的OnAuthenticationHelp方法是出现了可以回复的异常才会调用的。什么是可以恢复的异常呢?一个常见的例子就是:手指移动太快,当我们把手指放到传感器上的时候,如果我们很快地将手指移走的话,那么指纹传感器可能只采集了部分的信息,因此认证会失败。但是这个错误是可以恢复的,因此只要提示用户再次按下指纹,并且不要太快移走就可以解决。
- OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result)这个接口会在认证成功之后回调。我们可以在这个方法中提示用户认证成功。这里需要说明一下,如果我们上面在调用authenticate的时候,我们的CryptoObject不是null的话,那么我们在这个方法中可以通过AuthenticationResult来获得Cypher对象然后调用它的doFinal方法。doFinal方法会检查结果是不是会拦截或者篡改过,如果是的话会抛出一个异常。当我们发现这些异常的时候都应该将认证当做是失败来来处理,为了安全建议大家都这么做。
校验指纹功能是否可用
private boolean supportFingerprint() {
if (Build.VERSION.SDK_INT < 23) {
Toast.makeText(this, "您的系统版本过低,不支持指纹功能", Toast.LENGTH_SHORT).show();
return false;
} else {
KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
if (!fingerprintManager.isHardwareDetected()) {
Toast.makeText(this, "您的手机不支持指纹功能", Toast.LENGTH_SHORT).show();
return false;
}
// else if (!keyguardManager.isKeyguardSecure()) {
// Toast.makeText(this, "您还未设置锁屏,请先设置锁屏并添加一个指纹", Toast.LENGTH_SHORT).show();
// return false;
// }
else if (!fingerprintManager.hasEnrolledFingerprints()) {
Toast.makeText(this, "您至少需要在系统设置中添加一个指纹", Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}