一、概述
指紋識別通過指紋傳感器采集資訊,進行指紋影像的預處理,然后進行特征點提取,最后進行特征匹配,一般指紋識別的用途有:系統解鎖、應用鎖、支付認證、普通的登錄認證,
-
指紋識別兩種場景
本地識別:在本地完成指紋的識別后,跟本地資訊系結登陸;
后臺互動:在本地完成識別后,將資料傳輸到服務器;
無論是本地還是與服務器互動,都需要對資訊進行加密,通常來說,與本地互動的采用對稱加密,與服務器互動則采用非對稱加密,下面我們來簡單介紹下對稱加密和非對稱加密
-
對稱加密、非對稱加密和簽名
在正式使用指紋識別功能之前,有必要先了解一下對稱加密和非對稱加密的相關內容
對稱加密:

采用單密鑰密碼系統的方法,同一密鑰作為加密和解密的工具,通過密鑰控制加密和解密的指令,演算法規定如何加密和解密,優點是演算法公開、加密解密速度快、效率高,缺點是發送前的雙方保持統一密鑰,如果泄露則不安全,通常由AES、DES加密演算法等;
非對稱加密:

非對稱加密演算法需要兩個密鑰來進行加密和解密,這兩個秘鑰是公開密鑰(簡稱公鑰)和私有密鑰(簡稱私鑰),如果一方用公鑰進行加密,接受方應用私鑰進行解密,反之發送方用私鑰進行加密,接收方用公鑰進行解密,由于加密和解密使用的不是同一密鑰,故稱為非對稱加密演算法;與對稱加密演算法相比,非對稱加密的安全性得到了很大的提升,但是效率上則低了很多,因為解密加密花費的時間更長了,所以適合資料量少的加密,通常有RSA,ECC加密演算法等等
其中涉及到很多相關的加密演算法可參考:java安全之加密技術_itheimach的專欄-CSDN博客_java安全加密
Java使用Cipher類實作加密,包括DES,DES3,AES和RSA加密 - 蔡昭凱 - 博客園
Android保存私密資訊-強大的keyStore(譯) - 簡書
Android KeyStore + FingerprintManager 存盤密碼_lintcgirl的博客-CSDN博客_android keystore
二:指紋識別的兼容性和安全性問題
首先說兼容性,指紋識別的 API 是 Google 在 Android 6.0 開放出來的,
在 Android 6.0 以下的系統上,某些手機廠商自行支持了指紋識別,如果我們的 APP 要兼容這些設備,就還要集成廠商的指紋識別的SDK,這是最大的兼容性問題,不過,現在 Android 6.0 以下的設備已經很少了,其中支持指紋識別的設備就更少了,不對其進行兼容,我認為也是可以的,
在Android 6.0 以上的系統上,由于廠商對 Android 系統和指紋識別模塊的定制化普遍,導致會出現一些兼容性問題,這個沒有什么好的辦法,就需要開發者見招拆招了,已經踩過坑的開發者很多,大家可以到網上搜索相關的文章看,
然后說下安全性,由于已添加的指紋是存盤在手機上的,Google API 驗證指紋后僅僅回傳 true 或者 false,我們是很難無條件相信這個識別結果的,比如說用戶的手機 root 了或者是自定制設備,指紋識別是有可能被劫持進而回傳有誤的識別結果的,
好在這種情況發生的概率比較低,如果指紋識別的應用場景非交易非支付,僅僅是類似于 “啟動 APP 進行指紋驗證” 這樣的情況的話,Google API 提供的指紋識別就夠用了,
三:兼容安卓6.0、9.0的指紋識別
指紋識別是在Android 6.0以后新增的功能,在使用的時候需要先判斷手機的系統版本是否為安卓6.0以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {}
- AndroidManifest添加權限
<!--AndroidP(9.0)時 生物識別權限-->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!--AndroidP(6.0)時 開啟觸摸傳感器與身份認證的權限-->
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
-
檢查設別是否支持指紋識別
/**
* 檢查設別是否支持指紋識別
*
* @return
*/
public static SupportResult checkSupport(Context context) {
//指紋系統服務
FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
//判斷硬體是否支持指紋
if (!fingerprintManager.isHardwareDetected()) {
return SupportResult.DEVICE_UNSUPPORTED;
}
KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
//判斷是否處于安全保護中(你的設備必須是使用螢屏鎖保護的,這個螢屏鎖可以是password,PIN或者圖案都行) 判斷 是否開啟鎖屏密碼
if (!keyguardManager.isKeyguardSecure()) {
return SupportResult.SUPPORT_WITHOUT_KEYGUARD;
}
//設備支持且有指紋資料
if (fingerprintManager.hasEnrolledFingerprints()) {
return SupportResult.SUPPORT;
}
//設備支持指紋識別但是沒有指紋資料
return SupportResult.SUPPORT_WITHOUT_DATA;
}
1、先判斷硬體是否支持指紋識別
指紋識別肯定要求你的設備上有指紋識別的硬體,因此在運行時需要檢查系統當中是不是有指紋識別的硬體
2、判斷是否處于安全保護中
(你的設備必須是使用螢屏鎖保護的,這個螢屏鎖可以是password,PIN或者圖案都行) 即判斷 是否開啟鎖屏密碼,如果未設定則指導用戶跳轉手機設定頁面進行設定,

3、系統中是不是有注冊的指紋
在android 6.0中,普通app要想使用指紋識別功能的話,用戶必須首先在setting中注冊至少一個指紋才行,否則是不能使用的,如果未設定則指導用戶跳轉手機設定頁面進行設定,
- 跳轉設定頁面
根據不同的手機,跳轉到指紋錄入界面,如果沒有檢測到手機的品牌,就提醒用戶手動去指紋錄入
Android-引導用戶指紋錄入 - Android原創 - 博客園
四:根據6.0、9.0分別呼叫不同的API進行指紋識別
一:基于Android 6.0 實作指紋識別
方法authenticate
采用FingerprintManager類,進行指紋識別中最關鍵的方法authenticate,調起指紋識別掃描器進行指紋識別
FingerprintManager.authenticate(CryptoObject crypto, CancellationSignal cancel,
int flags, AuthenticationCallback callback, Handler handler);
我們來看一下這個介面:
上圖是google的api檔案中的描述,現在我們挨個解釋一下這些引數都是什么:
- crypto這是一個加密類的物件
指紋掃描器會使用這個物件來判斷認證結果的合法性,這個物件可以是null,但是這樣的話,就意味這app無條件信任認證的結果,雖然從理論上這個程序可能被攻擊,資料可以被篡改,這是app在這種情況下必須承擔的風險,因此,建議這個引數不要置為null,這個類的實體化有點麻煩,主要使用javax的security介面實作,后面我的demo程式中會給出一個helper類,這個類封裝內部實作的邏輯,開發者可以直接使用我的類簡化實體化的程序,
- cancel 這個是CancellationSignal類的一個物件
這個物件用來在指紋識別器掃描用戶指紋的是時候取消當前的掃描操作,如果不取消的話,那么指紋掃描器會移植掃描直到超時(一般為30s,取決于具體的廠商實作),
不及時取消的話,指紋掃描器就會一直掃描,直至超時,這會造成兩個問題:
(1) 耗電;
(2) 在超時時間內,用戶將無法再次調起指紋識別,
同樣,這個引數在 Android 6.0 是 @Nullable,在 Android 9.0 之后是 @NonNull ,由于上述的原因,不建議傳 null ,
- flags 標識位
根據上圖的檔案描述,這個位暫時應該為0,這個標志位應該是保留將來使用的,
- callback 這個是FingerprintManager.AuthenticationCallback類的物件
這個是這個介面中除了第一個引數之外最重要的引數了,當系統完成了指紋認證程序(失敗或者成功都會)后,會回呼這個物件中的介面,通知app認證的結果,這個引數不能為NULL,
- handler 這是Handler類的物件
如果這個引數不為null的話,那么FingerprintManager將會使用這個handler中的looper來處理來自指紋識別硬體的訊息,通常來講,開發這不用提供這個引數,可以直接置為null,因為FingerprintManager會默認使用app的main looper來處理,
取消指紋掃描
上面我們提到了取消指紋掃描的操作,這個操作是很常見的,這個時候可以使用CancellationSignal這個類的cancel方法實作: 
這個方法專門用于發送一個取消的命令給特定的監聽器,讓其取消當前操作,
因此,app可以在需要的時候呼叫cancel方法來取消指紋掃描操作
創建CryptoObject類物件
上面我們分析FingerprintManager的authenticate方法的時候,看到這個方法的第一個引數就是CryptoObject類的物件,現在我們看一下這個物件怎么去實體化,
我們知道,指紋識別的結果可靠性是非常重要的,我們肯定不希望認證的程序被一個第三方以某種形式攻擊,因為我們引入指紋認證的目的就是要提高安全性,但是,從理論角度來說,指紋認證的程序是可能被第三方的中間件惡意攻擊的,常見的攻擊的手段就是攔截和篡改指紋識別器提供的結果,這里我們可以提供CryptoObject物件給authenticate方法來避免這種形式的攻擊,
FingerprintManager.CryptoObject
是基于Java加密API的一個包裝類,并且被FingerprintManager用來保證認證結果的完整性,通常來講,用來加密指紋掃描結果的機制就是一個Javax.Crypto.Cipher物件,Cipher物件本身會使用由應用呼叫Android keystore的API產生一個key來實作上面說道的保護功能,
創建密鑰
創建密鑰要涉及到兩個類:KeyStore(俗稱密鑰商店) 和 KeyGenerator(密鑰發電機)
三個類:
KeyGenerator產生密鑰
KeyStore存放獲取密鑰
Cipher,是一個按照一定的加密規則,將資料進行加密后的一個物件
KeyStore 是用于存盤、獲取密鑰(Key)的容器,獲取 KeyStore的方法如下:
try {
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (KeyStoreException e) {
throw new RuntimeException("Failed to get an instance of KeyStore", e);
}
產生密鑰
/**
* @des 根據當前指紋庫創建一個密鑰
*/
void createKey() {
// 創建KeyGenerator物件
mKeyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
//生成密鑰引數 1.別名,這個名字可以是任意的 2.設定意圖,是加密還是解密
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(KEYSTORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
//保證了只有指定的block模式下可以加密,解密資料,如果使用其它的block模式,將會被拒絕
.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
mKeyGenerator.init(builder.build());
//生成了SecretKey(加密密鑰成功)
mKeyGenerator.generateKey();
}
} catch (Exception e) {
e.printStackTrace();
}
}
創建并初始化 Cipher 物件
Cipher 物件是一個按照一定的加密規則,將資料進行加密后的一個物件,呼叫指紋識別功能需要使用到這個物件,創建 Cipher 物件很簡單,如同下面代碼那樣:
/**
* @des 創建cipher (Cipher類提供了加密和解密的功能) AES/CBC/PKCS7Padding
*/
public Cipher createCipher() {
try {
//getInstance(String transformation) 回傳實作指定轉換的 Cipher 物件,
// 引數按"演算法/模式/填充模式"
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
然后使用剛才創建好的密鑰,初始化 Cipher 物件:
/**
* @des 初始化Cipher ,根據KeyPermanentlyInvalidatedExceptiony例外判斷指紋庫是否發生了變化
*/
public boolean initCipher(Cipher cipher) {
try {
keyStore.load(null);
//獲取密鑰
SecretKey key = (SecretKey) keyStore.getKey(KEYSTORE_ALIAS, null);
if (cipher == null) {
cipher = createCipher();
}
//ENCRYPT_MODE 加密模式,key為密鑰 進行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
return false;
} catch (KeyPermanentlyInvalidatedException | UnrecoverableKeyException e) {
//該密鑰已被永久無效 |未能獲得有關私鑰
//指紋庫是否發生了變化,這里會拋KeyPermanentlyInvalidatedException
return true;
} catch (KeyStoreException | CertificateException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
//密鑰庫例外|
// throw new RuntimeException("Failed to init Cipher", e);
e.printStackTrace();
return true;
} catch (Exception e) {
e.printStackTrace();
return true;
}
}
這里需要強調一點,在以下情況下,android會認為當前key是無效的:
1. 一個新的指紋image已經注冊到系統中
2. 當前設備中的曾經注冊過的指紋現在不存在了,可能是被全部洗掉了
3. 用戶關閉了螢屏鎖功能
4. 用戶改變了螢屏鎖的方式
當上面的情況發生的時候,Cipher.init方法都會拋出KeyPermanentlyInvalidatedException的例外
處理用戶的指紋認證結果
前面我們分析authenticate介面的時候說道,呼叫這個介面的時候必須提供FingerprintManager.AuthenticationCallback類的物件,這個物件會在指紋認證結束之后系統回呼以通知app認證的結果的,在android 6.0中,指紋的掃描和認證都是在另外一個行程中完成(指紋系統服務)的,因此底層什么時候能夠完成認證我們app是不能假設的,因此,我們只能采取異步的操作方式,也就是當系統底層完成的時候主動通知我們,通知的方式就是通過回呼我們自己實作的FingerprintManager.AuthenticationCallback類,這個類中定義了一些回呼方法以供我們進行必要的處理:
下面我們簡要介紹一下這些介面的含義:
1. OnAuthenticationError(int errorCode, ICharSequence errString)
這個介面會再系統指紋認證出現不可恢復的錯誤的時候才會呼叫,并且引數errorCode就給出了錯誤碼,標識了錯誤的原因,測驗了下一般是OnAuthenticationFailed出現5次,會進入OnAuthenticationError回呼中,接下來再次進行識別時會一直回呼這個方法,這個時候app能做的只能是提示用戶重新嘗試一遍,即指紋傳感器會關閉一段時間,在下次呼叫authenticate時,會出現禁用期(時間依廠商不同30s,1分都有)
2. OnAuthenticationFailed()
這個介面會在系統指紋認證失敗的情況的下才會回呼,注意這里的認證失敗和上面的認證錯誤是不一樣的,雖然結果都是不能認證,認證失敗是指所有的資訊都采集完整,并且沒有任何例外,但是這個指紋和之前注冊的指紋是不相符的;但是認證錯誤是指在采集或者認證的程序中出現了錯誤,比如指紋傳感器作業例外等,也就是說認證失敗是一個可以預期的正常情況,而認證錯誤是不可預期的例外情況,
3. OnAuthenticationHelp(int helpMsgId, ICharSequence helpString)
上面的認證失敗是認證程序中的一個例外情況,我們說那種情況是因為出現了不可恢復的錯誤,而我們這里的OnAuthenticationHelp方法是出現了可以恢復=復的例外才會呼叫的,什么是可以恢復的例外呢?一個常見的例子就是:手指移動太快,當我們把手指放到傳感器上的時候,如果我們很快地將手指移走的話,那么指紋傳感器可能只采集了部分的資訊,因此認證會失敗,但是這個錯誤是可以恢復的,因此只要提示用戶再次按下指紋,并且不要太快移走就可以解決,
4. OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result)
這個介面會在認證成功之后回呼,我們可以在這個方法中提示用戶認證成功,這里需要說明一下,如果我們上面在呼叫authenticate的時候,我們的CryptoObject不是null的話,那么我們在這個方法中可以通過AuthenticationResult來獲得Cypher物件然后呼叫它的doFinal方法,doFinal方法會檢查結果是不是會攔截或者篡改過,如果是的話會拋出一個例外,當我們發現這些例外的時候都應該將認證當做是失敗來來處理,為了安全建議大家都這么做,

關于上面的介面還有2點需要補充一下:
1. 上面我們說道OnAuthenticationError 和 OnAuthenticationHelp方法中會有錯誤或者幫助碼以提示為什么認證不成功,Android系統定義了幾個錯誤和幫助碼在FingerprintManager類中,如下
我們的callback類實作的時候最好需要處理這些錯誤和幫助碼, 具體大家可以詳細了解下,
2. 當指紋掃描器正在作業的時候,如果我們取消本次操作的話,系統也會回OnAuthenticationError方法的,只是這個時候的錯誤碼是FingerprintManager.FINGERPRINT_ERROR_CANCELED(值為5、指紋操作以取消),如果要區別判斷,則大家可以去獲取對呀的值進行區別判斷,

基于Android 9.0 實作指紋識別
在AndroidP(9.0)時候,官方不再推薦使用FingerprintManager,標記為@Deprecated,開放BiometricPrompt新的API
AndroidManifest添加權限
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
Android 9.0及以上的指紋認證實作,相比Android 6.0 指紋識別框可以自定義,而9.0不允許開發者自定義指紋識別框
this.mBiometricPrompt = new BiometricPrompt
.Builder(activity)
.setTitle("我是指紋彈出窗的標題")
.setDescription("我是它的詳細資訊")
.setNegativeButton("按鈕名稱 一般是取消",
activity.getMainExecutor(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSelfCanceled = true;
mFingerCallback.onCancel();
mCancellationSignal.cancel();
}
})
.build();
指紋識別關鍵方法 authenticate
BiometricPrompt.authenticate(@NonNull CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback)
引數和6.0的差不多,
- Executor executor (補充,是)
這個引數是 Android 9.0 Api BiometricPrompt.authenticate() 中的引數,是 @NonNull 的,作用與上個引數 Handler handler 類似,用來分發指紋識別的回呼事件,
當通過主執行緒進行分發時,可通過 Context#getMainExecutor() 傳參;
當通過共享執行緒池進行分發時,可通過 AsyncTask#THREAD_POOL_EXECUTOR 傳參,
本文相關的事例代碼可自行進行下載:GitHub - WCaiZhu/FingerprintRecognition: 有關指紋識別的工具包,適配6.0和9.0以上的安卓設備
可查看更多有關指紋的文章:
Android6.0指紋解鎖demo_小白的究極進化-CSDN博客
指紋登錄 - 徐繼收 - 博客園
Android開發學習—指紋識別系統的原理與使用_程式病毒小隊的博客-CSDN博客Android 指紋識別,提升APP用戶體驗,從這里開始_牛角尖-CSDN博客
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/304358.html
標籤:其他
