主要用到的是FingerprintManager这个类,此类是访问指纹硬件的便捷类,通过
Context#getSystemService(Context.FINGERPRINT_SERVICE)获取相应的单例。
应用场景:在指纹加密模块中对一个具有某种特定功能的数据(比如密码等)进行加密;在其他需要进行密码验证的地方,通过指纹解密模块将加密的数据还原后验证。
首先在Manifest.xml中添加权限申请: <uses-permission android:name="android.permission.USE_FINGERPRINT" /> 注意:该权限为基础权限,只需要在Manifest.xml中添加即可,不需要动态权限申请。
一般情况下,在开启指纹功能时,系统会要求用户添加备用PIN码、图案或密码。因为有时候(例如重启设备或系统无法识别指纹时),将需要使用这种方式来解锁设备。
先按照系统提示录入指纹。
1. 判断是否支持指纹
需要了解的是,系统的指纹功能是从Android6.0开始正式引入的,所以在使用指纹功能之前进行必要的需要判断。
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
showDialog("该手机不支持指纹功能");
return;
}
Context mContext = this;
if (checkSelfPermission(Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
showDialog("需要获取使用指纹的权限");
return;
}else {
FingerprintManager manager = (FingerprintManager) mContext.getSystemService(Context.FINGERPRINT_SERVICE);
KeyguardManager keyManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
//硬件设备是否支持指纹解锁功能
if (!manager.isHardwareDetected()) {
showDialog("该手机不支持指纹解锁");
return;
}
//判断是否有锁屏密码
else if (!keyManager.isKeyguardSecure()) {
showDialog("请先设置锁屏密码");
return;
}
//判断是否录入指纹
else if (!manager.hasEnrolledFingerprints()) {
showDialog("没有录入指纹,请录入指纹后重试");
return;
}
}
此demo UI比较简单,在MainActivity中有两个按钮,一个的功能为跳转到设置指纹密码页面FingerPrintSettingActivity,另一个为指纹解码功能 :
设置指纹密码页面为:
未添加指纹密码时,如图
添加后,可以删除和重置
LocalSharedPreference 类为通过sp储存必要数据的便捷类:
public class LocalSharedPreference {
final String dataKeyName = "data";
final String IVKeyName = "IV"; //Initialization Vector (IV) 初始化向量
private SharedPreferences preferences;
LocalSharedPreference(Context context) {
preferences = context.getSharedPreferences("sample", Activity.MODE_PRIVATE);
}
String getData(String keyName) {
//同样,在读取SharedPreferences数据前要实例化出一个SharedPreferences对象
return preferences.getString(keyName, "");
}
boolean storeData(String key, String data) {
SharedPreferences.Editor editor = preferences.edit();
editor.putString(key, data);
return editor.commit();
}
boolean containsKey(String key) {
return !TextUtils.isEmpty(getData(key));
}
}
LocalSharedPreference类主要用于 获取类型为"AndroidKeyStore"的KeyStore、生成Key和CryptoObject。
public class LocalAndroidKeyStore {
private KeyStore mStore;
public static final String keyName = "key";
LocalAndroidKeyStore() {
try {
mStore = KeyStore.getInstance("AndroidKeyStore");
} catch (Exception e) {
e.printStackTrace();
}
}
@TargetApi(23)
void generateKey(String keyAlias) {
try {
final KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
mStore.load(null);
//先取值,取不到时才生成新的key
final SecretKey key = (SecretKey) mStore.getKey(keyName, null);
if (key != null) {
return ;
}
final int purpose = KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT;
final KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyAlias, purpose);
builder.setUserAuthenticationRequired(true);
builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC);
builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
generator.init(builder.build());
generator.generateKey();
} catch (Exception e) {
e.printStackTrace();
}
}
@TargetApi(23)
FingerprintManager.CryptoObject getCryptoObject(int purpose, byte[] IV) {
try {
mStore.load(null);
final SecretKey key = (SecretKey) mStore.getKey(keyName, null);
if (key == null) {
return null;
}
final Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
if (purpose == KeyProperties.PURPOSE_ENCRYPT) {
cipher.init(purpose, key);
} else {
cipher.init(purpose, key, new IvParameterSpec(IV));
}
return new FingerprintManager.CryptoObject(cipher);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
FingerPrintHelper为对FingerprintManager的封装类,加解密的处理逻辑在这里:
@TargetApi(23)
public class FingerPrintHelper extends FingerprintManager.AuthenticationCallback {
private FingerprintManager manager;
private CancellationSignal mCancellationSignal;
private SimpleAuthenticationCallback callback;
private LocalSharedPreference mLocalSharedPreference;
private LocalAndroidKeyStore mLocalAndroidKeyStore;
//PURPOSE_ENCRYPT,则表示生成token,否则为取出token
private int purpose = KeyProperties.PURPOSE_ENCRYPT;
private String data = "";
private static FingerPrintHelper mFingerHelper;
public void setData(String data) {
this.data = data;
}
public static synchronized FingerPrintHelper getInstance(Context context){
if(mFingerHelper == null){
mFingerHelper = new FingerPrintHelper(context);
}
return mFingerHelper;
}
@TargetApi(23)
public FingerPrintHelper(Context context) {
manager = context.getSystemService(FingerprintManager.class);
mLocalSharedPreference = new LocalSharedPreference(context);
mLocalAndroidKeyStore = new LocalAndroidKeyStore();
generateKey();
}
public void generateKey() {
//在keystore中生成加密密钥
mLocalAndroidKeyStore.generateKey(LocalAndroidKeyStore.keyName);
}
public void setCallback(SimpleAuthenticationCallback callback) {
this.callback = callback;
}
public void setPurpose(int purpose) {
this.purpose = purpose;
}
@TargetApi(23)
public boolean authenticate() {
try {
FingerprintManager.CryptoObject object;
if (purpose == KeyProperties.PURPOSE_DECRYPT) {
String IV = mLocalSharedPreference.getData(mLocalSharedPreference.IVKeyName);
object = mLocalAndroidKeyStore.getCryptoObject(Cipher.DECRYPT_MODE, Base64.decode(IV, Base64.URL_SAFE));
if (object == null) {
return false;
}
} else {
object = mLocalAndroidKeyStore.getCryptoObject(Cipher.ENCRYPT_MODE, null);
}
mCancellationSignal = new CancellationSignal();
manager.authenticate(object, mCancellationSignal, 0, this, null);
return true;
} catch (SecurityException e) {
e.printStackTrace();
return false;
}
}
@TargetApi(16)
public void stopAuthenticate() {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
mCancellationSignal = null;
}
callback = null;
}
@TargetApi(23)
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
if (callback == null) {
return;
}
if (result.getCryptoObject() == null) {
callback.onAuthenticationFail();
return;
}
final Cipher cipher = result.getCryptoObject().getCipher();
if (purpose == KeyProperties.PURPOSE_DECRYPT) {
//取出secret key并返回
String data = mLocalSharedPreference.getData(mLocalSharedPreference.dataKeyName);
if (TextUtils.isEmpty(data)) {
callback.onAuthenticationFail();
return;
}
try {
byte[] decrypted = cipher.doFinal(Base64.decode(data, Base64.URL_SAFE));
callback.onAuthenticationSucceeded(new String(decrypted));
} catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
callback.onAuthenticationFail();
}
} else {
//将前面生成的data包装成secret key,存入沙盒
try {
byte[] encrypted = cipher.doFinal(data.getBytes());
byte[] IV = cipher.getIV();
String se = Base64.encodeToString(encrypted, Base64.URL_SAFE);
//保存解密需要的IV变量
String siv = Base64.encodeToString(IV, Base64.URL_SAFE);
if (mLocalSharedPreference.storeData(mLocalSharedPreference.dataKeyName, se) &&
mLocalSharedPreference.storeData(mLocalSharedPreference.IVKeyName, siv)) {
callback.onAuthenticationSucceeded(se);
}else{
callback.onAuthenticationFail();
}
} catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
callback.onAuthenticationFail();
}
}
}
public void clearData(){
mLocalSharedPreference.storeData(mLocalSharedPreference.dataKeyName, null);
mLocalSharedPreference.storeData(mLocalSharedPreference.IVKeyName, null);
}
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
if (callback != null) {
callback.onAuthenticationFail();
}
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
}
@Override
public void onAuthenticationFailed() {
}
public interface SimpleAuthenticationCallback {
void onAuthenticationSucceeded(String value);
void onAuthenticationFail();
}
}
这样,在FingerPrintSettingActivity中,实例化FingerPrintHelper类对象helper:
helper = FingerPrintHelper.getInstance(context);
helper.setCallback(context);
并实现FingerPrintHelper.SimpleAuthenticationCallback接口:
@Override
public void onAuthenticationSucceeded(String value) {
CommonFunction.dismissProgressDialog();
sp.edit().putString("FingerprintPwd", value).commit();
CommonFunction.showToast("指纹密码设置成功");
finish();
}
@Override
public void onAuthenticationFail() {
CommonFunction.dismissProgressDialog();
showDialog("指纹密码设置失败");
}
其他变量设置:
sp = getSharedPreferences("fingerprint", MODE_PRIVATE);
pwd = sp.getString("FingerprintPwd", "");
if(!TextUtils.isEmpty(pwd)) {
finger_list_layout.setVisibility(View.VISIBLE);
reset_pwd.setText("重置指纹密码");
}
/**
* 对密码加密
*/
private void encodePwdOperation(String pwd){
CommonFunction.showProgressDialog(context, "开始验证指纹,请将手指贴与指纹区域...");
helper.setPurpose(KeyProperties.PURPOSE_ENCRYPT);
helper.setData(pwd);
helper.authenticate();
}
/**
* 删除密码
*/
private void delPwdOperation(){
CustomConfirmDialog.Builder builder = new CustomConfirmDialog.Builder(this);
builder.setTitle("确认");
builder.setMessage("确定删除该指纹密码?");
builder.setPositiveBtn("确定",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
finger_list_layout.setVisibility(View.GONE);
//TODO 删除已加密数据
sp.edit().putString("FingerprintPwd", "").commit();
pwd = "";
reset_pwd.setText("添加指纹密码");
helper.clearData();
}
});
builder.setNegativeBtn("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
}
/**
* 设置/重置密码
*/
private void setPwdOperation(){
if(TextUtils.isEmpty(pwd)) {
encodePwdOperation();
}else {
CustomConfirmDialog.Builder builder = new CustomConfirmDialog.Builder(this);
builder.setTitle("确认");
builder.setMessage("确定重置指纹密码?");
builder.setPositiveBtn("确定",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
//TODO 删除已加密数据
sp.edit().putString("FingerprintPwd", "").commit();
pwd = "";
helper.clearData();
encodePwdOperation();
}
});
builder.setNegativeBtn("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
}
}
解码页面,同样实现FingerPrintHelper.SimpleAuthenticationCallback接口:
/*解码过程*/
helper = FingerPrintHelper.getInstance(context);
helper.setCallback(this);
helper.setPurpose(KeyProperties.PURPOSE_DECRYPT);
if(helper != null) {
helper.authenticate();
}
效果图:
一. 首次添加密码时:
二. 删除密码再添加时:
版权声明:本文为博主原创文章,未经博主允许不得转载;来自https://blog.csdn.net/milanac007/article/details/79714843