指紋識別-Android

指紋識別-Android

@(Android進階資料)[Android, 學(xué)習(xí), 讀書筆記, Markdown]
指紋識別是在Android6.0之后新增的功能,所以在使用的時候首先要判斷用戶的系統(tǒng)版本是否支持指紋識別雕凹。另外,實際開發(fā)場景中,使用指紋的主要場景有兩種:

  • 純本地使用庆寺。即用戶在本地完成指紋識別后,不需要將指紋信息傳遞給后臺。
  • 與后臺交互。用戶在本地完成指紋識別后,需要將指紋信息傳遞給后臺趴泌。

由于使用指紋識別功能需要一個加密對象(CryptoObject),該對象一般是由對稱加密或者非對稱加密獲得泄私。上述兩種應(yīng)用場景的實現(xiàn)大同小異,主要區(qū)別在于加密過程中密鑰的創(chuàng)建和使用,一般來說,純本地的指紋識別功能,只需要對稱加密即可粥烁;而與后臺則需要使用非對稱加密:將私鑰用于本地指紋識別,識別成功后將加密信息傳給后臺,后臺用公鑰解密,以獲得用戶信息见秽。

對稱加密、非對稱加密和簽名

在正式使用指紋識別功能之前,有必要先了解一下對稱加密和非對稱加密的相關(guān)內(nèi)容是嗜。

  • 對稱加密:所謂對稱愈案,就是采用這種加密方法的雙方使用方式用同樣的密鑰進行加密和解密。密鑰是控制加密及解密過程的指令鹅搪。算法是一組規(guī)則站绪,規(guī)定如何進行加密和解密。因此加密的安全性不僅取決于加密算法本身丽柿,密鑰管理的安全性更是重要恢准。因為加密和解密都使用同一個密鑰,如何把密鑰安全地傳遞到解密者手上就成了必須要解決的問題甫题。
  • 非對稱加密:非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)馁筐。公開密鑰與私有密鑰是一對,如果用公開密鑰對數(shù)據(jù)進行加密坠非,只有用對應(yīng)的私有密鑰才能解密敏沉;如果用私有密鑰對數(shù)據(jù)進行加密,那么只有用對應(yīng)的公開密鑰才能解密炎码。因為加密和解密使用的是兩個不同的密鑰盟迟,所以這種算法叫作非對稱加密算法。 非對稱加密算法實現(xiàn)機密信息交換的基本過程是:甲方生成一對密鑰并將其中的一把作為公用密鑰向其它方公開潦闲;得到該公用密鑰的乙方使用該密鑰對機密信息進行加密后再發(fā)送給甲方攒菠;甲方再用自己保存的另一把專用密鑰對加密后的信息進行解密。
  • 簽名:在信息的后面再加上一段內(nèi)容歉闰,可以證明信息沒有被修改過辖众。一般是對信息做一個hash計算得到一個hash值,注意和敬,這個過程是不可逆的凹炸,也就是說無法通過hash值得出原來的信息內(nèi)容。在把信息發(fā)送出去時概龄,把這個hash值加密后做為一個簽名和信息一起發(fā)出去还惠。

由以上內(nèi)容可以了解到饲握,對稱加密和非對稱加密的特點如下:

  • 對稱加密的優(yōu)點是速度快私杜,適合于本地數(shù)據(jù)和本地數(shù)據(jù)庫的加密,安全性不如非對稱加密救欧。常見的對稱加密算法有DES衰粹、3DESAES笆怠、Blowfish铝耻、IDEARC5RC6瓢捉。
  • 非對稱加密的安全性比較高频丘,適合對需要網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)進行加密,速度不如對稱加密泡态。非對稱加密應(yīng)用于SSH, HTTPS, TLS搂漠,電子證書電子簽名某弦,電子身份證等等

指紋識別的對稱加密實現(xiàn)

使用指紋識別的對稱加密功能的主要流程如下:

  1. 使用 KeyGenerator 創(chuàng)建一個對稱密鑰桐汤,存放在 KeyStore 里。
  2. 設(shè)置 KeyGenParameterSpec.Builder.setUserAuthenticationRequired() 為true
  3. 使用創(chuàng)建好的對稱密鑰初始化一個Cipher對象靶壮,并用該對象調(diào)用 FingerprintManager.authenticate() 方法啟動指紋傳感器并開始監(jiān)聽怔毛。
  4. 重寫 FingerprintManager.AuthenticationCallback 的幾個回調(diào)方法,以處理指紋識別成功(onAuthenticationSucceeded())腾降、失敿鸲取(onAuthenticationFailed()onAuthenticationError())等情況。

創(chuàng)建密鑰

創(chuàng)建密鑰要涉及到兩個類: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 對象比較簡單蚁滋,方法如下:

// 對稱加密宿接, 創(chuàng)建 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();
}

創(chuàng)建并初始化 Cipher 對象

Cipher 對象是一個按照一定的加密規(guī)則辕录,將數(shù)據(jù)進行加密后的一個對象睦霎。調(diào)用指紋識別功能需要使用到這個對象。創(chuàng)建 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("創(chuàng)建Cipher對象失敗", e);
}

然后使用剛才創(chuàng)建好的密鑰副女,初始化 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);
}

使用指紋識別功能

真正到了使用指紋識別功能的時候,你會發(fā)現(xiàn)其實很簡單蚣旱,只是調(diào)用 FingerprintManager 類的的方法authenticate()而已碑幅,然后系統(tǒng)會有相應(yīng)的回調(diào)反饋給我們,該方法如下:

public void authenticate(CryptoObject crypto, CancellationSignal cancel, int flags, AuthenticationCallback callback, Handler handler)

該方法的幾個參數(shù)解釋如下:

  • 第一個參數(shù)是一個加密對象塞绿。還記得之前我們大費周章地創(chuàng)建和初始化的Cipher對象嗎沟涨?這里的 CryptoObject 對象就是使用 Cipher 對象創(chuàng)建創(chuàng)建出來的:new FingerprintManager.CryptoObject(cipher)
  • 第二個參數(shù)是一個 CancellationSignal 對象异吻,該對象提供了取消操作的能力裹赴。創(chuàng)建該對象也很簡單,使用 new CancellationSignal() 就可以了。
  • 第三個參數(shù)是一個標志棋返,默認為0延都。
  • 第四個參數(shù)是 AuthenticationCallback 對象,它本身是 FingerprintManager 類里面的一個抽象類睛竣。該類提供了指紋識別的幾個回調(diào)方法窄潭,包括指紋識別成功、失敗等酵颁。需要我們重寫嫉你。
  • 最后一個 Handler,可以用于處理回調(diào)事件躏惋,可以傳null幽污。

完成指紋識別后,還要記得將 AuthenticationCallback 關(guān)閉掉:

public void stopListening() {
    if (cancellationSignal != null) {
        selfCancelled = true;
        cancellationSignal.cancel();
        cancellationSignal = null;
    }
}

重寫回調(diào)方法

調(diào)用了 authenticate() 方法后簿姨,系統(tǒng)就會啟動指紋傳感器距误,并開始掃描。這時候根據(jù)掃描結(jié)果扁位,會通過FingerprintManager.AuthenticationCallback類返回幾個回調(diào)方法:

// 成功
onAuthenticationSucceeded()
// 失敗
onAuthenticationFaile()
// 錯誤
onAuthenticationError()

一般我們需要重寫這幾個方法准潭,以實現(xiàn)我們的功能。關(guān)于onAuthenticationFaile()和onAuthenticationError()的區(qū)別域仇,后面會講到刑然。

實際應(yīng)用中的注意事項

判斷用戶是否可以使用指紋識別功能

一般來說,為了增加安全性暇务,要求用戶在手機的“設(shè)置”中開啟了密碼鎖屏功能泼掠。當然,使用指紋解鎖的前提是至少錄入了一個指紋垦细。
// 如果沒有設(shè)置密碼鎖屏择镇,則不能使用指紋識別

if (!keyguardManager.isKeyguardSecure()) {
    Toast.makeText(this, "請在設(shè)置界面開啟密碼鎖屏功能",
            Toast.LENGTH_LONG).show();
}
// 如果沒有錄入指紋,則不能使用指紋識別
if (!fingerprintManager.hasEnrolledFingerprints()) {
    Toast.makeText(this, "您還沒有錄入指紋, 請在設(shè)置界面錄入至少一個指紋",
            Toast.LENGTH_LONG).show();
}

這里用到了兩個類:KeyguardManagerFingerprintManager括改,前者是屏幕保護的相關(guān)類腻豌。后者是指紋識別的核心類。

關(guān)于指紋識別回調(diào)方法

前面說到AuthenticationCallback類里面的幾個回調(diào)方法嘱能,其中有三個是我們開發(fā)中需要用到的:

onAuthenticationError()
onAuthenticationSucceeded()
onAuthenticationFailed()

關(guān)于這三個回調(diào)方法吝梅,有幾點需要注意的:

  1. 當指紋識別失敗后,會調(diào)用onAuthenticationFailed()方法焰檩,這時候指紋傳感器并沒有關(guān)閉憔涉,系統(tǒng)給我們提供了5次重試機會订框,也就是說析苫,連續(xù)調(diào)用了5次onAuthenticationFailed()方法后,會調(diào)用onAuthenticationError()方法。
  2. 當系統(tǒng)調(diào)用了onAuthenticationError()onAuthenticationSucceeded()后衩侥,傳感器會關(guān)閉国旷,只有我們重新授權(quán),再次調(diào)用authenticate()方法后才能繼續(xù)使用指紋識別功能茫死。
  3. 當系統(tǒng)回調(diào)了onAuthenticationError()方法關(guān)閉傳感器后跪但,這種情況下再次調(diào)用authenticate()會有一段時間的禁用期,也就是說這段時間里是無法再次使用指紋識別的峦萎。當然屡久,具體的禁用時間由手機廠商的系統(tǒng)不同而有略微差別,有的是1分鐘爱榔,有的是30秒等等被环。而且,由于手機廠商的系統(tǒng)區(qū)別详幽,有些系統(tǒng)上調(diào)用了onAuthenticationError()后筛欢,在禁用時間內(nèi),其他APP里面的指紋識別功能也無法使用唇聘,甚至系統(tǒng)的指紋解鎖功能也無法使用版姑。而有的系統(tǒng)上,在禁用時間內(nèi)調(diào)用其他APP的指紋解鎖功能迟郎,或者系統(tǒng)的指紋解鎖功能剥险,就能立即重置指紋識別功能。

示例代碼

最后宪肖, Android Sample 里面關(guān)于指紋的示例代碼地址如下:

完整代碼

package com.zichenjiao.testapp;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

@TargetApi(Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private final static int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 0;
    Button testfingerprint;
    CancellationSignal mCancellationSignal = new CancellationSignal();
    FingerprintManager manager;
    KeyguardManager mKeyManager;
    //回調(diào)方法
    FingerprintManager.AuthenticationCallback mSelfCancelled = new FingerprintManager.AuthenticationCallback() {
        @Override
        public void onAuthenticationError(int errorCode, CharSequence errString) {
            //但多次指紋密碼驗證錯誤后炒嘲,進入此方法;并且匈庭,不能短時間內(nèi)調(diào)用指紋驗證
            Toast.makeText(MainActivity.this, errString, Toast.LENGTH_SHORT).show();
            showAuthenticationScreen();
        }

        @Override
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {

            Toast.makeText(MainActivity.this, helpString, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {

            Toast.makeText(MainActivity.this, "指紋識別成功", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onAuthenticationFailed() {
            Toast.makeText(MainActivity.this, "指紋識別失敗", Toast.LENGTH_SHORT).show();
        }
    };
    private String TAG = "TAG";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testfingerprint = (Button) findViewById(R.id.testfingerprint);
        testfingerprint.setOnClickListener(this);
        //初始化指紋識別管理器
        manager = (FingerprintManager) this.getSystemService(Context.FINGERPRINT_SERVICE);
        mKeyManager = (KeyguardManager) this.getSystemService(Context.KEYGUARD_SERVICE);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.testfingerprint://測試指紋識別
                if (isFinger()) {
                    Toast.makeText(MainActivity.this, "請進行指紋識別", Toast.LENGTH_LONG).show();
                    Log(TAG, "keyi");
                    startListening(null);
                }
                break;
        }
    }

    public boolean isFinger() {

        //android studio 上夫凸,沒有這個會報錯
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "沒有指紋識別權(quán)限", Toast.LENGTH_SHORT).show();
            return false;
        }
        Log(TAG, "有指紋權(quán)限");
        //判斷硬件是否支持指紋識別
        if (!manager.isHardwareDetected()) {
            Toast.makeText(this, "沒有指紋識別模塊", Toast.LENGTH_SHORT).show();
            return false;
        }
        Log(TAG, "有指紋模塊");
        //判斷 是否開啟鎖屏密碼

        if (!mKeyManager.isKeyguardSecure()) {
            Toast.makeText(this, "沒有開啟鎖屏密碼", Toast.LENGTH_SHORT).show();
            return false;
        }
        Log(TAG, "已開啟鎖屏密碼");
        //判斷是否有指紋錄入
        if (!manager.hasEnrolledFingerprints()) {
            Toast.makeText(this, "沒有錄入指紋", Toast.LENGTH_SHORT).show();
            return false;
        }
        Log(TAG, "已錄入指紋");

        return true;
    }

    public void startListening(FingerprintManager.CryptoObject cryptoObject) {
        //android studio 上,沒有這個會報錯
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "沒有指紋識別權(quán)限", Toast.LENGTH_SHORT).show();
            return;
        }
        manager.authenticate(cryptoObject, mCancellationSignal, 0, mSelfCancelled, null);
    }

    /**
     * 鎖屏密碼
     */
    private void showAuthenticationScreen() {

        Intent intent = mKeyManager.createConfirmDeviceCredentialIntent("finger", "測試指紋識別");
        if (intent != null) {
            startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
            // Challenge completed, proceed with using cipher
            if (resultCode == RESULT_OK) {
                Toast.makeText(this, "識別成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "識別失敗", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void Log(String tag, String msg) {
        Log.d(tag, msg);
    }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阱持,一起剝皮案震驚了整個濱河市夭拌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衷咽,老刑警劉巖鸽扁,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異镶骗,居然都是意外死亡桶现,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門鼎姊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骡和,“玉大人相赁,你說我怎么就攤上這事∥坑冢” “怎么了钮科?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長婆赠。 經(jīng)常有香客問我绵脯,道長,這世上最難降的妖魔是什么休里? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任蛆挫,我火速辦了婚禮,結(jié)果婚禮上妙黍,老公的妹妹穿的比我還像新娘璃吧。我一直安慰自己,他們只是感情好废境,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布畜挨。 她就那樣靜靜地躺著,像睡著了一般噩凹。 火紅的嫁衣襯著肌膚如雪巴元。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天驮宴,我揣著相機與錄音逮刨,去河邊找鬼。 笑死堵泽,一個胖子當著我的面吹牛修己,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迎罗,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼睬愤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纹安?” 一聲冷哼從身側(cè)響起尤辱,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厢岂,沒想到半個月后光督,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡塔粒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年结借,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卒茬。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡船老,死狀恐怖咖熟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情努隙,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布辜昵,位于F島的核電站荸镊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏堪置。R本人自食惡果不足惜躬存,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舀锨。 院中可真熱鬧岭洲,春花似錦、人聲如沸坎匿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽替蔬。三九已至告私,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間承桥,已是汗流浹背驻粟。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凶异,地道東北人蜀撑。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像剩彬,于是被迫代替她去往敵國和親酷麦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 最近項目需要使用到指紋識別的功能喉恋,查閱了相關(guān)資料后票唆,整理成此文。 指紋識別是在Android 6.0之后新增的功能...
    湫水長天閱讀 3,738評論 2 46
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理枯芬,服務(wù)發(fā)現(xiàn)宵荒,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 目錄 準備 分析2.1. 三次握手2.2. 創(chuàng)建 HTTP 代理(非必要)2.3. TLS/SSL 握手2.4. ...
    RunAlgorithm閱讀 38,158評論 12 117
  • 本文主要介紹移動端的加解密算法的分類苔悦、其優(yōu)缺點特性及應(yīng)用轩褐,幫助讀者由淺入深地了解和選擇加解密算法。文中會包含算法的...
    蘋果粉閱讀 11,503評論 5 29
  • 觸手可及之處玖详, 天還是原來的天把介, 我還是原來的我勤讽。 匆匆歲月, 變化的是人心拗踢, 不變的是心境脚牍。 起風了, 天不再是...
    落花有春閱讀 232評論 3 1