標籤:
轉載請註明出處:http://blog.csdn.net/wl9739/article/details/52444671
最近項目需要使用到指紋識別的功能,查閱了相關資料後,整理成此文。
指紋識別是在Android 6.0之後新增的功能,因此在使用的時候需要先判斷使用者手機的系統版本是否支援指紋識別。另外,實際開發情境中,使用指紋的主要情境有兩種:
- 純本地使用。即使用者在本地完成指紋識別後,不需要將指紋的相關資訊給後台。
- 與後台互動。使用者在本地完成指紋識別後,需要將指紋相關的資訊傳給後台。
由於使用指紋識別功能需要一個加密對象(CryptoObject)該對象一般是由對稱式加密或者非對稱式加密獲得。上述兩種開發情境的實現大同小異,主要區別在於加密過程中密鑰的建立和使用,一般來說,純本地的使用指紋識別功能,只需要對稱式加密即可;而與後台互動則需要使用非對稱式加密:將私密金鑰用於本地指紋識別,識別成功後將加密資訊傳給後台,後台開發人員用公開金鑰解密,以獲得使用者資訊。
下面先簡單介紹一下對稱式加密和非對稱式加密的相關概念,然後對兩種開發方式的實現分別進行講解。
對稱式加密、非對稱式加密和簽名
在正式使用指紋識別功能之前,有必要先瞭解一下對稱式加密和非對稱式加密的相關內容。
對稱式加密:所謂對稱,就是採用這種加密方法的雙方使用方式用同樣的密鑰進行加密和解密。密鑰是控制加密及解密過程的指令。演算法是一組規則,規定如何進行加密和解密。因此加密的安全性不僅取決於密碼編譯演算法本身,密鑰管理的安全性更是重要。因為加密和解密都使用同一個密鑰,如何把密鑰安全地傳遞到解密者手上就成了必須要解決的問題。
非對稱式加密:非對稱式加密演算法需要兩個密鑰:公開密鑰(publickey)和私人密鑰(privatekey)。公開密鑰與私人密鑰是一對,如果用公開金鑰組資料進行加密,只有用對應的私人密鑰才能解密;如果用私人金鑰組資料進行加密,那麼只有用對應的公開密鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種演算法叫作非對稱式加密演算法。 非對稱式加密演算法實現機密資訊交換的基本過程是:甲方產生一對密鑰並將其中的一把作為公用密鑰向其它方公開;得到該公用密鑰的乙方使用該金鑰組機密資訊進行加密後再發送給甲方;甲方再用自己儲存的另一把專用金鑰組加密後的資訊進行解密。
簽名:在資訊的後面再加上一段內容,可以證明資訊沒有被修改過。一般是對資訊做一個hash計算得到一個hash值,注意,這個過程是無法復原的,也就是說無法通過hash值得出原來的資訊內容。在把資訊發送出去時,把這個hash值加密後做為一個簽名和資訊一起發出去。
由以上內容可以瞭解到,對稱式加密和非對稱式加密的特點如下:
- 對稱式加密的優點是速度快,適合於本機資料和本機資料庫的加密,安全性不如非對稱式加密。常見的對稱式加密演算法有DES、3DES、AES、Blowfish、IDEA、RC5、RC6。
- 非對稱式加密的安全性比較高,適合對需要網路傳輸的資料進行加密,速度不如對稱式加密。非對稱式加密應用於SSH, HTTPS, TLS,電子認證,電子簽名,電子身份證等等
指紋識別的對稱式加密實現
使用指紋識別的對稱式加密功能的主要流程如下:
- 使用 KeyGenerator 建立一個對稱金鑰,存放在 KeyStore 裡。
- 設定
KeyGenParameterSpec.Builder.setUserAuthenticationRequired() 為true,
- 使用建立好的對稱金鑰初始化一個Cipher對象,並用該對象調用
FingerprintManager.authenticate() 方法啟動指紋感應器並開始監聽。
- 重寫
FingerprintManager.AuthenticationCallback 的幾個回調方法,以處理指紋識別成功(onAuthenticationSucceeded())、失敗(onAuthenticationFailed() 和 onAuthenticationError())等情況。
建立密鑰
建立密鑰要涉及到兩個類:KeyStore 和 KeyGenerator。
KeyStore 是用於儲存、擷取密鑰(Key)的容器,擷取 KeyStore的方法如下:
try { mKeyStore = KeyStore.getInstance("AndroidKeyStore");} catch (KeyStoreException e) { throw new RuntimeException("Failed to get an instance of KeyStore", e);}
而產生 Key,如果是對稱式加密,就需要 KeyGenerator 類。擷取一個 KeyGenerator 對象比較簡單,方法如下:
// 對稱式加密, 建立 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 了:
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 對象很簡單,如同下面代碼那樣:
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 對象:
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()而已,然後系統會有相應的回調反饋給我們,該方法如下:
public void authenticate(CryptoObject crypto, CancellationSignal cancel, int flags, AuthenticationCallback callback, Handler handler)
該方法的幾個參數解釋如下:
- 第一個參數是一個加密對象。還記得之前我們大費周章地建立和初始化的Cipher對象嗎?這裡的 CryptoObject 對象就是使用 Cipher 對象建立建立出來的:
new FingerprintManager.CryptoObject(cipher)。
- 第二個參數是一個 CancellationSignal 對象,該對象提供了取消操作的能力。建立該對象也很簡單,使用
new CancellationSignal() 就可以了。
- 第三個參數是一個標誌,預設為0。
- 第四個參數是 AuthenticationCallback 對象,它本身是 FingerprintManager 類裡面的一個抽象類別。該類提供了指紋識別的幾個回調方法,包括指紋識別成功、失敗等。需要我們重寫。
- 最後一個 Handler,可以用於處理回調事件,可以傳null。
完成指紋識別後,還要記得將 AuthenticationCallback 關閉掉:
public void stopListening() { if (cancellationSignal != null) { selfCancelled = true; cancellationSignal.cancel(); cancellationSignal = null; }}
重寫回調方法
調用了 authenticate() 方法後,系統就會啟動指紋感應器,並開始掃描。這時候根據掃描結果,會通過FingerprintManager.AuthenticationCallback類返回幾個回調方法:
// 成功onAuthenticationSucceeded()// 失敗onAuthenticationFaile()// 錯誤onAuthenticationError()
一般我們需要重寫這幾個方法,以實現我們的功能。關於onAuthenticationFaile()和onAuthenticationError()的區別,後面會講到。
指紋識別的非對稱式加密實現
其實流程和上面的流程差不多:
- 使用 KeyPairGenerator 建立一個非對稱金鑰。
- 使用建立好的私密金鑰進行簽名,使用該簽名建立一個加密對象,並將該對象作為
FingerprintManager.authenticate() 方法的一個參數,啟動指紋感應器並開始監聽。
- 重寫
FingerprintManager.AuthenticationCallback 類的幾個回調方法,以處理指紋識別成功(onAuthenticationSucceeded())、失敗(onAuthenticationFailed() 和 onAuthenticationError())等情況。
可以看見,指紋識別的非對稱式加密方式和對稱式加密方式的實現流程是差不多的,它們之間最明顯的差別是在於密鑰的產生與使用。
建立密鑰
這裡要使用 KeyPairGenerator 來建立一組非對稱金鑰,首先是擷取 KeyPairGenerator 對象:
// 非對稱式加密,建立 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(金鑰組)了:
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對象:
// 使用私密金鑰簽名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()中處理的邏輯不一樣。
實際應用中的注意事項判斷使用者是否可以使用指紋識別功能
一般來說,為了增加安全性,要求使用者在手機的“設定”中開啟了密碼鎖屏功能。當然,使用指紋解鎖的前提是至少錄入了一個指紋。
// 如果沒有設定密碼鎖屏,則不能使用指紋識別if (!keyguardManager.isKeyguardSecure()) { Toast.makeText(this, "請在設定介面開啟密碼鎖屏功能", Toast.LENGTH_LONG).show();}// 如果沒有錄入指紋,則不能使用指紋識別if (!fingerprintManager.hasEnrolledFingerprints()) { Toast.makeText(this, "您還沒有錄入指紋, 請在設定介面錄入至少一個指紋", Toast.LENGTH_LONG).show();}
這裡用到了兩個類:KeyguardManager 和 FingerprintManager,前者是螢幕保護裝置的相關類。後者是指紋識別的核心類。
關於指紋識別回調方法
前面說到AuthenticationCallback類裡面的幾個回調方法,其中有三個是我們開發中需要用到的:
onAuthenticationError()onAuthenticationSucceeded()onAuthenticationFailed()
關於這三個回調方法,有幾點需要注意的:
當指紋識別失敗後,會調用onAuthenticationFailed()方法,這時候指紋感應器並沒有關閉,系統給我們提供了5次重試機會,也就是說,連續調用了5次onAuthenticationFailed()方法後,會調用onAuthenticationError()方法。
當系統調用了onAuthenticationError()和onAuthenticationSucceeded()後,感應器會關閉,只有我們重新授權,再次調用authenticate()方法後才能繼續使用指紋識別功能。
當系統回調了onAuthenticationError()方法關閉感應器後,這種情況下再次調用authenticate()會有一段時間的禁用期,也就是說這段時間裡是無法再次使用指紋識別的。當然,具體的禁用時間由手機廠商的系統不同而有略微差別,有的是1分鐘,有的是30秒等等。而且,由於手機廠商的系統區別,有些系統上調用了onAuthenticationError()後,在禁用時間內,其他APP裡面的指紋識別功能也無法使用,甚至系統的指紋解鎖功能也無法使用。而有的系統上,在禁用時間內調用其他APP的指紋解鎖功能,或者系統的指紋解鎖功能,就能立即重設指紋識別功能。
範例程式碼
最後, Android Sample 裡面關於指紋的範例程式碼地址如下:
對稱式加密方式:android-FingerprintDialog。
非對稱式加密方式:android-AsymmetricFingerprintDialog
參考連結:New in Android Samples: Authenticating to remote servers using the Fingerprint API
Android中的指紋識別