Android 指纹识别(Touch ID)实例
指纹识别
指纹识别的支持是Android6.0以后才开始的,Google也为指纹识别提供了一些列接口,指纹识别将要用到的核心API为FingerprintManager,其中还有三个核心内部类:FingerprintManager.AuthenticationResult 指纹识别后结果的回调,FingerprintManager.AuthenticationCallback指纹识别成功失败回调, FingerprintManager.CryptoObject指纹识别加密对象。其中最难实现的为CryptoObject的创建。为了加强指纹识别的安全级别我们还可以对需要传送的密码进行加密,通过KeyStore非对称加密实现。
FingerprintManager
方法名 | 参数 | 描述 |
---|---|---|
authenticate() | CryptoObject,CancellationSignal,flags,AuthenticationCallback,Handler | 用于开启指纹识别 |
isHardwareDetected() | 无 | 判断指纹识别硬件是否存在且能正常使用 |
hasEnrolledFingerprints() | 无 | 确定是否至少注册了一个指纹 |
- CryptoObject 用于加密对象(可null)
- CancellationSignal 用于取消指纹识别(可null)
- flags 用作标记
- AuthenticationCallback 指纹识别回调方法(可null)
- Handler (可null)
如果只是简单测试可以只传入flags 和 AuthenticationCallback 即可
使用实例
(1)权限申明
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
(2)获取FingerprintManager对象
/*** 获取FingerprintManager** @return FingerprintManager*/@RequiresApi(api = Build.VERSION_CODES.M)public FingerprintManager getFingerprintManagerOrNull() {if (getApplication().getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {return getApplication().getSystemService(FingerprintManager.class);} else {return null;}}
(3)创建指纹识别回调对象
/*** 指纹识别回调监听*/private FingerprintManager.AuthenticationCallback callback = new FingerprintManager.AuthenticationCallback() {@Overridepublic void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {mTouchIdDialog.dismiss();//指纹验证成功Toast.makeText(MainActivity.this, "指纹验证成功", Toast.LENGTH_SHORT).show();}@Overridepublic void onAuthenticationError(int errorCode, CharSequence errString) {mTouchIdDialog.dismiss();//指纹验证失败,不可再验Toast.makeText(MainActivity.this, "onAuthenticationError:" + errString, Toast.LENGTH_SHORT).show();}@Overridepublic void onAuthenticationHelp(int helpCode, CharSequence helpString) {mTouchIdDialog.startIconShackAnimation();//指纹验证失败,可再验,可能手指过脏,或者移动过快等原因。Toast.makeText(MainActivity.this, "onAuthenticationHelp:" + helpString, Toast.LENGTH_SHORT).show();}@Overridepublic void onAuthenticationFailed() {mTouchIdDialog.startIconShackAnimation();//指纹验证失败,指纹识别失败,可再验,该指纹不是系统录入的指纹。Toast.makeText(MainActivity.this, "无法识别", Toast.LENGTH_SHORT).show();}};
(4)创建CancellationSignal用于取消指纹识别,开启指纹识别。
if (mFingerprintManager.isHardwareDetected() && mFingerprintManager.hasEnrolledFingerprints()) {mTouchIdStartBtn.setClickable(false);if (mTouchIdDialog == null) {mTouchIdDialog = new TouchIdDialog(MainActivity.this, R.style.TouchIdDialog);mTouchIdDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {@Overridepublic void onDismiss(DialogInterface dialog) {//如果dialog消失则取消指纹识别if (mCancellationSignal != null && isStartAuthenticate) {isStartAuthenticate = false;mCancellationSignal.cancel();mCancellationSignal = null;}mTouchIdStartBtn.setClickable(true);}});}mTouchIdDialog.show();mCancellationSignal = new CancellationSignal();if(mCryptoObjectCreator==null){initCryptoObject();}else {//开始验证指纹mFingerprintManager.authenticate(null,mCancellationSignal, 0, callback, null);isStartAuthenticate = true;}}
到目前为止
一个简单的指纹识别demo我们就做好了,但是你们会看到我们并没用创建FingerprintManager.CryptoObject对象来进行加密,所以这样的识别是不安全的,在某种情况下会被其他具有威胁的应用破解,所以我们必须创建一个简单的指纹识别demo我们就做好了,但是你们会看到我们并没用创建FingerprintManager.CryptoObject对象来进行加密。
通过Cipher来创建FingerprintManager.CryptoObject对象
我们将通过Cipher和KeyStore来创建加密密钥,如果应用A创建了一个key,然后应用B通过AndroidKeyStore去获取该key,是获取不到的,这样我们也就实现了指纹识别的加密。我们也可以通过keyStore来对密码进行加密。通过keyStore加密后的数据可以随意存储于任意位置即使是sharedperference中,因为即使获取到数据也获取不到密钥去对数据进行解密。
(1)创建KeyStore并初始化
private void initKeyStore(String alias){try {keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);}catch(Exception e) {e.printStackTrace();}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {createNewKeys(alias);}}
(2)创建KeyPair用于非对称加密
private void createNewKeys(String alias){if(!"".equals(alias)){try {// Create new key if neededif (!keyStore.containsAlias(alias)) {Calendar start = Calendar.getInstance();Calendar end = Calendar.getInstance();end.add(Calendar.YEAR, 1);KeyPairGeneratorSpec spec = null;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {spec = new KeyPairGeneratorSpec.Builder(IApplication.getApplication()).setAlias(alias).setSubject(new X500Principal("CN=Sample Name, O=Android Authority")).setSerialNumber(BigInteger.ONE).setStartDate(start.getTime()).setEndDate(end.getTime()).build();}KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {generator.initialize(spec);}KeyPair keyPair = generator.generateKeyPair();}} catch (Exception e) {e.printStackTrace();}}}
(3)获取Cipher用于创建FingerprintManager.CryptoObject
public Cipher getCipher(String alias){KeyStore.PrivateKeyEntry privateKeyEntry = null;try {privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());return output;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnrecoverableEntryException e) {e.printStackTrace();} catch (KeyStoreException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();}return null;}
这样FingerprintManager.CryptoObject对象我们就创建完成了,其使用方法如下
mFingerprintManager.authenticate(new FingerprintManager.CryptoObject(EncryUtils.getInstance().getCipher(mAlias)), mCancellationSignal, 0, callback, null);
KeyStore非对称加密解密
(1)加密
/*** 加密方法* @param needEncryptWord 需要加密的字符串* @param alias 加密秘钥* @return*/public String encryptString(String needEncryptWord, String alias) {if(!"".equals(alias)&&!"".equals(needEncryptWord)){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {initKeyStore(alias);}String encryptStr="";byte [] vals=null;try {KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);if(needEncryptWord.isEmpty()) {return encryptStr;}Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");inCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());ByteArrayOutputStream outputStream = new ByteArrayOutputStream();CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inCipher);cipherOutputStream.write(needEncryptWord.getBytes("UTF-8"));cipherOutputStream.close();vals = outputStream.toByteArray();} catch (Exception e) {e.printStackTrace();}return Base64.encodeToString(vals, Base64.DEFAULT);}return "";}
(2)解密
/*** 解密方法* @param needDecryptWord 需要解密的字符串* @param alias key的别称* @return*/public String decryptString(String needDecryptWord, String alias) {if(!"".equals(alias)&&!"".equals(needDecryptWord)){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {initKeyStore(alias);}String decryptStr="";try {KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());CipherInputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(Base64.decode(needDecryptWord, Base64.DEFAULT)), output);ArrayList<Byte> values = new ArrayList<>();int nextByte;while ((nextByte = cipherInputStream.read()) != -1) {values.add((byte)nextByte);}byte[] bytes = new byte[values.size()];for(int i = 0; i < bytes.length; i++) {bytes[i] = values.get(i).byteValue();}decryptStr = new String(bytes, 0, bytes.length, "UTF-8");} catch (Exception e) {e.printStackTrace();}return decryptStr;}return "";}
完整KeyStore工具类代码
package com.daobao.asus.touchiddemo.keyStoreUtil;import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.util.Base64;import com.daobao.asus.touchiddemo.IApplication;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableEntryException;
import java.util.ArrayList;
import java.util.Calendar;import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.security.auth.x500.X500Principal;/*** Created by xiongyu on 2016/12/1.* 使用ksyStore加密工具类*/public class EncryUtils {static EncryUtils encryUtilsInstance;KeyStore keyStore;public static EncryUtils getInstance() {synchronized (EncryUtils.class) {if (null == encryUtilsInstance) {encryUtilsInstance = new EncryUtils();}}return encryUtilsInstance;}private EncryUtils() {}private void initKeyStore(String alias){try {keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null);}catch(Exception e) {e.printStackTrace();}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {createNewKeys(alias);}}private void createNewKeys(String alias){if(!"".equals(alias)){try {// Create new key if neededif (!keyStore.containsAlias(alias)) {Calendar start = Calendar.getInstance();Calendar end = Calendar.getInstance();end.add(Calendar.YEAR, 1);KeyPairGeneratorSpec spec = null;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {spec = new KeyPairGeneratorSpec.Builder(IApplication.getApplication()).setAlias(alias).setSubject(new X500Principal("CN=Sample Name, O=Android Authority")).setSerialNumber(BigInteger.ONE).setStartDate(start.getTime()).setEndDate(end.getTime()).build();}KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {generator.initialize(spec);}KeyPair keyPair = generator.generateKeyPair();}} catch (Exception e) {e.printStackTrace();}}}/*** 加密方法* @param needEncryptWord 需要加密的字符串* @param alias 加密秘钥* @return*/public String encryptString(String needEncryptWord, String alias) {if(!"".equals(alias)&&!"".equals(needEncryptWord)){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {initKeyStore(alias);}String encryptStr="";byte [] vals=null;try {KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);if(needEncryptWord.isEmpty()) {return encryptStr;}Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");inCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());ByteArrayOutputStream outputStream = new ByteArrayOutputStream();CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inCipher);cipherOutputStream.write(needEncryptWord.getBytes("UTF-8"));cipherOutputStream.close();vals = outputStream.toByteArray();} catch (Exception e) {e.printStackTrace();}return Base64.encodeToString(vals, Base64.DEFAULT);}return "";}/*** 解密方法* @param needDecryptWord 需要解密的字符串* @param alias key的别称* @return*/public String decryptString(String needDecryptWord, String alias) {if(!"".equals(alias)&&!"".equals(needDecryptWord)){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {initKeyStore(alias);}String decryptStr="";try {KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());CipherInputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(Base64.decode(needDecryptWord, Base64.DEFAULT)), output);ArrayList<Byte> values = new ArrayList<>();int nextByte;while ((nextByte = cipherInputStream.read()) != -1) {values.add((byte)nextByte);}byte[] bytes = new byte[values.size()];for(int i = 0; i < bytes.length; i++) {bytes[i] = values.get(i).byteValue();}decryptStr = new String(bytes, 0, bytes.length, "UTF-8");} catch (Exception e) {e.printStackTrace();}return decryptStr;}return "";}/*** 获取私钥* @param alias* @return*/public PrivateKey getprivateKey(String alias){initKeyStore(alias);try {KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);return privateKeyEntry.getPrivateKey();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnrecoverableEntryException e) {e.printStackTrace();} catch (KeyStoreException e) {e.printStackTrace();}return null;}public Cipher getCipher(String alias){KeyStore.PrivateKeyEntry privateKeyEntry = null;try {privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());return output;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnrecoverableEntryException e) {e.printStackTrace();} catch (KeyStoreException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();}return null;}
}
写在最后
以上代码并不完整如果需要完整代码可以到TouchID实例这个地址进行下载。
Android 指纹识别(Touch ID)实例相关推荐
- 【源码】iOS指纹解锁Touch ID的开发
指纹解锁原理: 苹果在文件中对iPhone 5S配备的Touch ID指纹识别系统的功能与工作原理进行了详细说明.文件对iPhone 5S的A7处理器中""Secure Encla ...
- Android指纹识别
Android指纹识别 原文:Android指纹识别 上一篇讲了通过FingerprintManager验证手机是否支持指纹识别,以及是否录入了指纹,这里进行指纹的验证. //获取Fingerprin ...
- android指纹识别源码
随着科技的进步,不仅是软件方面,安卓系统在硬件发展上也有了日新月异的发展.2015年下半年出的安卓手机基本上都带指纹识别功能,不管是炒得很热的360奇酷手机.魅族手机,还是"中华酷联&quo ...
- Android 指纹识别
和你一起终身学习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一.指纹类概述 二.指纹类权限 三.指纹类详细介绍 四.指纹使用实战 官方的指纹识别在Android 6 ...
- android获取指纹信息最新,# android 指纹识别并检测指纹库是否变更
android API 23时新增的功能,指纹识别 主要类:FingerpringManager 在API28后,FingerpringManager被遗弃,使用BiometricPrompt,此时授 ...
- android 指纹识别支付 secure os,Android指纹识别
常用开发工具类和自定义view,无耻的求个star: https://github.com/AbrahamCaiJin/CommonUtilLibrary 最近开始做项目的时候,需要用到Android ...
- Android指纹识别,看这一篇就够了
在Android6.0(Api23)的时候,Android系统加入了指纹识别的api接口,即FingerprintManager,定义了最基础的指纹识别接口.不过,在AndroidP(Api28)的时 ...
- ios与android指纹识别,iOS开发实现TouchID指纹解锁
一直想实现一下指纹解锁,苦于一直没时间,最近终于闲了下来所以翻了翻文档看了看demo,完成了这篇教程.本功能实现起来是很简单的,因为苹果都已经帮我们封装好了,只需要实现几个方法就可以了. 实现效果图 ...
- android指纹识别真机,就等魅蓝了,全面屏时代指纹解锁该放在哪才不尴尬
[PConline 杂谈]全面屏手机来临,市面上的手机似乎在一夜之间都成为了长相相差无几的"表兄弟",除了Logo可以证明不是出自一家之手,相同的正面外观.机身背部毫无区别的双摄和 ...
最新文章
- EXECL导入(检查服务器版本.包括NPOI方式导入.可以通过配置文件信息导入EXECL)代码记录下....
- 数据中心的“芯”竞争
- 你有一笔新订单 语音_上市即成爆款 哪吒V首日订单突破1200辆_搜狐汽车
- 检验开发团队好不好的12个问题
- 「每天一道面试题」如何理解方法的重载与覆盖?
- Nginx:Nginx limit_req limit_conn限速
- 判断三个数是否能构成三角形_三角形的面积
- 可变悬挂调节软硬_国六最亲民的豪车,丐版2.0T纯进口,全系可变悬架+8气囊,才23万...
- 【2】二级C语言中那些易错的概念题
- 让OSX terminal更出彩
- Flutter之跨组件状态共享Provider框架剖析(2)
- MEF程序设计指南四:使用MEF声明导出(Exports)与导入(Imports)
- java中的策略模式_简单了解Java中的策略模式
- python查找相似图片或重复图片
- 国际版链克口袋 获取方法
- 联想笔记本怎么重装系统?联想笔记本一键重装
- 树莓派CSI摄像头使用
- Vue.js如何获得兄弟元素,子元素,父元素(DOM操作)
- 洛谷P1427 小鱼的数字游戏c语言
- 无线节点的空中唤醒技术解析