想知道微信怎麼做指紋支付開發?看這裡!

騰訊雲加社群發表於2018-04-03

歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~

作者簡介:Henryye,葉軒,來自騰訊微信事業群,主要負責騰訊開源專案TENCENT SOTER(GitHub地址:https://github.com/Tencent/soter )生物認證平臺的開發、維護與運營。

提到指紋支付,你會怎麼做?

假如有一天,產品經理安排你做指紋支付,並且要下版本就上,你會怎麼做?

如果是產品大哥,就從工位下面抽出一把指甲刀架在他脖子上,讓他跪在牆角唱征服;

如果是產品妹子,就讓她請你喝咖啡,然後談天說地,趁此機會告訴她“還是選擇世界和平吧,比做指紋支付簡單多了。”

當然,想象還是太溫柔了。真正做過指紋支付專案的在下,經常會在半夜三更回憶起當年做指紋支付需求時候的噩夢,在夢裡,我就給自己加戲,手撕產品經理。

也許產品大大們會發出抗議:“指紋支付而已,客戶端現成的介面,有何難?”

系統介面行不行?

從2013年iPhone 5s第一款帶有指紋識別功能的iPhone上市以來,“指紋支付”這個詞就開始頻繁出現在各個產品的PM列表排期中。但是,Android 6.0以前的裝置,並沒有一個統一的指紋認證介面。這也就意味著如果你是一個苦逼的程式猿,那麼你就要一家家適配各自的指紋方案,並且還要維護廠商的介面升級。如果結合Android市場的碎片化來看,想要全機型覆蓋,簡直就是Impossible Mission。實際上,在專案初期,微信便嘗試了一家家接入,結果僅僅接入華為Mate 7和榮耀7,便用了整整三個月!這種投入顯然是得不償失的。

好在,從Android 6.0開始,系統提供了標準的FingerprintManager。這一重大利好,讓做著類似需求的程式猿們彷彿在黑暗中看到一絲光明,因為這個介面看上去是那麼簡單易用。無論你是什麼品牌的手機,只要是Android 6.0或更新的系統,按照下面的寫法,就可以實現指紋認證功能:

FingeprintManager mFingeprintManager = ...
mFingeprintManager.authenticate(null, 
mCancellationSignal, 0 , new AuthenticationCallback(){...},
 null);
複製程式碼
設計本身也很簡單:

系統認證介面

看上去很完美,彷彿實現指紋支付根本不用開發1個版本,只用1小時,對不對!

但是仔細看下這個介面,感覺哪裡不太對:介面僅僅返回認證成功/失敗,如果直接信任這個結果,手機被root了,豈不是隨時可以將認證結果從false改為true?

那我們換一種思路:root的手機不讓用指紋支付行不行?

傻孩子,那你怎麼判斷手機是不是root呢?不也是通過Android介面獲取的值麼?這個值一樣可以被改掉。

支付安全不可兒戲,一旦出問題,就是重大事故。所幸的是,Google也意識到了這個問題,所以在釋出指紋認證介面的同時,增強了原本的KeyStore介面,和Fingeprprint介面聯動(程式碼實現可參考Google官方Sample,連結:https://github.com/googlesamples/android-FingerprintDialog):

這張圖看上去不明覺厲,原理其實並不難:Google在Android 6.0之後,允許使用者在應用中生成一對非對稱金鑰,將私鑰儲存在TEE中(什麼是TEE?稍後會講),任何人,包括應用自己甚至Android系統都無法獲取私鑰,除非使用者使用指紋授權才能使用,簽名或者加密傳入的資料,然後輸出密文。這樣的話,就可以利用金鑰的簽名-驗籤機制(小白不懂什麼是簽名驗籤?Google下咯~或者看這篇文章解釋簽名的部分),只有使用者使用指紋簽名之後,才能產生正確的簽名,後臺驗籤即可,這樣就能保證鏈路安全。

這個設計非常巧妙,但是Google百密一疏:如果黑客在金鑰生成的時候就攔截了請求,替換為自己的金鑰,那麼後面簽名和加密,用的也是黑客的金鑰,那麼整套系統的設計也就崩塌了。

Hack示意

另外還有一個問題,如果僅僅返回true/false,那麼只要是錄入到裝置內的指紋,就可以假冒你的身份支付。這對於家裡有熊孩子的家長來說,簡直就是銀行卡噩夢。雪上加霜的是,對於Android裝置而言(其實iOS也是一樣),只要知道了鎖屏密碼就可以錄入新的指紋。如果支付後臺直接信任指紋認證結果,就相當於將原本非常祕密的支付密碼,退化到了鎖屏密碼的級別。這樣,無論支付後臺做了多麼嚴密的風控策略,按照木桶原理,從根本上整個系統就是不符合支付安全的。

當然,當時也有類似於FIDO(連結:https://fidoalliance.org/)之類的認證聯盟,但是整個流程過於複雜,甚至還要求在應用後臺植入sdk。而且,類似方案的中心服務許可權過高,會導致如支付筆數、開通使用者數等關鍵指標為人所知,因此也就無法使用。並且支援裝置數實在太少,也並無接入動力。

研究過這些之後,發現並不可直接使用任何一個方案,場面一度尷尬。沒有合適的輪子,怎麼辦?

沒有輪子,能造輪子麼?

讓我們回頭看看Android系統的指紋介面設計:

  • 方便的指紋介面,完美!
  • 創造性得將指紋模組與金鑰模組結合起來,使得使用者授權即簽名變得可能,完美!

那Google沒有做到什麼呢?

  • 由於沒有一個可信的信任根,導致金鑰很容易被替換;
  • 無法從認證結果中獲取到底是哪一個使用者授權本次認證請求;

同時,我們意識到,在生物認證領域這個千億級市場中,缺乏一個統一、安全、易接入的認證標準,微信有這樣的需求,其他應用也必然如此。微信有能力解決這些問題,實現自己的業務需求,也希望將成功經驗複製。既然這樣,藉此機會制定一個生物認證標準,提供一個生物認證平臺,微信責無旁貸,這就是SOTER的起源。

如果以做標準的要求來實現SOTER,那麼除了剛剛所述的系統介面缺陷之外,系統設計時還需要考慮:

  • 後臺不儲存任何敏感資訊,包括對稱金鑰、非對稱金鑰私鑰,更不能將使用者生態無特徵(如指紋圖案)以任何形式傳輸或儲存,防止應用後臺被脫庫;
  • 如果有後臺互動,不暴露應用方核心商業隱私,如認證次數、業務開通次數;
  • 應用接入門檻低,客戶端無須整合重量級sdk,後臺無須整合sdk;
  • 簡單易用,第三方應用只需要操作上層介面,無須進行復雜的底層開發。

如何產生一個可信的信任根(裝置根金鑰)?

信任根的重要性之前已經說明。如果一個系統依賴金鑰簽名,有一個可以信任的根金鑰,才有可能構建安全的信任模型。但是,如果一臺手機出廠之後才產生根金鑰(ATTK),那麼中間有足夠時間視窗給到黑產從業者替換掉它。因此,常規方式產生根金鑰一定是不行的。所以,我們有了一個大膽的想法:直接與廠商合作,在裝置出廠之前,產線上生成裝置根金鑰,公鑰通過廠商服務傳輸給微信金鑰服務——TAM。這個想法雖然對廠商改造比較大,但是由於我們直接通產業鏈上游(高通、MTK等)合作,研發出了一套統一的解決方案,以及產線工具,成功說服廠商對產線做了最小化的改造。It’s tough, but we did it!

裝置根金鑰流程

  1. 廠商在產線上對裝置下發生成裝置根金鑰命令;
  2. TEE中生成一對裝置唯一的RSA-2048非對稱金鑰,私鑰儲存在裝置RPMB區域,沒有任何廠商或者應用方可以讀取(包括微信與裝置廠商),公鑰以及裝置ID匯出;
  3. 公鑰和裝置ID上傳到廠商伺服器,之後通過公眾平臺安全介面,傳輸到微信公眾平臺;
  4. 微信公眾平臺將公鑰與裝置ID傳輸到微信TAM伺服器。

這裡,我們又遇到了我們的老朋友:TEE(Trusted Execution Environment),後面我們也會多次與這個名詞打交道。如果想要看詳細的介紹,可以參考這裡(連結:https://en.wikipedia.org/wiki/Trusted_execution_environment)。當然了,我相信大部分同學都跟我一樣,只想要一個形象的解釋。簡單地說,你的手機中,除了類似Android這樣的作業系統之外,還有一個獨立的環境。這個環境目前並無行之有效的破解方法,也就是說即使Root了Android系統,都無法破解TEE中的資料。如果將整部手機比作房子的話,Android操作環境就是客廳,TEE就是你的保險箱。可想而知,如果將所有的資料都儲存在TEE,關鍵操作也在TEE內進行,豈不美哉!當然了,這樣的話,所有從TEE中出來的敏感資料,就一定要新增上使用可信金鑰對其的簽名了。

有了裝置根金鑰之後,認證鏈的構造邏輯就清晰了很多:採用金鑰鏈的形式,用已認證的金鑰來認證未認證的金鑰就可以了!

如何構造完整認證流程?

方法論有了,實施就變得簡單。

但是,依然有一個問題需要思考:到底需要多少層金鑰呢?金鑰層數少,那麼每一次都需要前往中心服務(微信TAM)驗籤,對第三方應用而言,會更擔心洩露自己的商業邏輯;金鑰層數越多,會增加了傳輸複雜度和失敗率。經過多方討論,SOTER決定使用三級金鑰,除了產線預製的裝置根金鑰之外,增加定義應用金鑰(每一個應用生命週期內只需要存在一對)和業務金鑰(每一個業務需要一對)。事實證明,這是一個明智的選擇:這樣既保證了流程的流暢度,又保證應用的關鍵商業隱私不暴露。為什麼?往下看。

架構

SOTER架構圖

我們十分欣賞Google的指紋和金鑰模組介面設計,因此,我們與廠商合作,在此基礎上新增patch包,即可迅速實現整個上層架構。

準備應用金鑰(ASK)

準備應用金鑰流程示意圖

  1. 應用第一次啟動時,或者在第一次使用業務之前,請求生成裝置根金鑰;
  2. 金鑰生成之後,私鑰在被TEE保護,加密儲存;
  3. 公鑰和裝置ID等相關資訊,在TEE內直接被裝置金鑰私鑰簽名之後,返回給應用;
  4. 應用將公鑰相關資訊和簽名傳輸至應用後臺;
  5. 應用後臺通過微信公眾平臺後臺介面,請求驗籤;
  6. TAM使用對應的裝置金鑰公鑰驗籤,通過之後返回給應用;
  7. 應用後臺儲存對應的應用公鑰。

傳輸給後臺的原串示例:

注1:自裝置出廠即在TEE中儲存。每一次SOTER相關操作都會使該值自增,後臺儲存該放重放因子。如果後臺發現本次請求防重放因子比已記錄的值小,則可認為是非法請求。

注2:本意為類Unix系統中使用者ID,在Android系統中,一般而言每一個應用都有一個uid,可用於區分應用以及許可權控制。注意,uid不同,對應的應用金鑰與業務金鑰均不同,後臺應將uid與cpu_id一起區分金鑰。

準備業務金鑰(Auth Key)

準備業務金鑰流程示意圖

  1. 應用在開通業務時(如指紋支付),請求生成業務金鑰。同時,在生成時宣告該金鑰除非使用者指紋授權,否則私鑰不可使用。
  2. 金鑰生成之後,私鑰在被TEE保護,加密儲存;
  3. 公鑰和裝置ID等相關資訊,在TEE內直接被應用金鑰私鑰簽名之後,返回給應用;
  4. 應用將公鑰相關資訊和簽名傳輸至應用後臺;
  5. 應用後臺使用對應的應用金鑰公鑰驗籤,無須請求微信TAM中心服務;
  6. 驗籤成功之後,應用後臺儲存對應的業務金鑰。

傳輸資料與含義與應用金鑰相同,不再贅述。

認證流程

認證流程示意圖

  1. 客戶端請求後臺,獲取挑戰因子;
  2. 獲取挑戰因子之後,將挑戰因子送往TEE,準備簽名結構體,準備簽名;
  3. 應用請求使用者指紋授權;
  4. 使用者指紋授權後,直接將本次認證使用指紋在本裝置內的索引傳輸給金鑰模組,在TEE內使用業務金鑰私鑰簽名挑戰因子以及該索引。

應用獲取原串與簽名串後,傳輸至應用後臺。應用後臺使用對應的業務金鑰公鑰驗籤,如果成功,則此次認證或者開通請求合法。

傳輸給後臺原串示例

流程是否符合要求?

輪子造好了,我們在自我欣賞的路上越走越遠。回過頭來看,SOTER是否滿足了我們的要求呢?

  • 新增信任根:SOTER在工廠環境中傳輸裝置根金鑰,保證信任根安全;
  • 可區分指紋:認證之後,TEE內部直接傳輸本次使用的指紋ID,可使應用自由選擇是否區分指紋;
  • 後臺不儲存敏感資訊:後臺僅儲存裝置ID和公鑰,從此即使後臺設計再爛,也不再害怕脫庫;
  • 後臺互動不暴露隱私:獨創應用金鑰,保證業務開通、業務使用量等不需要經過應用伺服器;
  • 後臺不需要sdk:後臺使用成熟的公眾平臺介面,文件豐富,學習成本低,更無須sdk。

當然了,我們的方案得到了各大廠商、晶片上的認可,在短時間內,已經擁有了數億裝置的支援,覆蓋幾乎所有的主流手機品牌,因此應用接入完全無須考慮是否需要多裝置適配,或者質疑適配不足。順便,我們支援了vivo和OPPO的5.x指紋機型,即使系統本身不具有統一的指紋介面。

然而,還有最後兩點沒有做到:

  • 客戶端接入門檻低,客戶端sdk輕量,甚至不需要sdk;
  • 簡單易用,客戶端無須進行深度開發即可使用。

解決這兩個問題的方法只有:開源!

我們開源了什麼?

為了滿足不同應用的不同場景,我們開源了:

  • SOTER客戶端核心介面soter-core:SOTER內部操作金鑰、呼叫指紋的直接介面。sdk大小約40KB,對安裝包大小增量可忽略不計;
  • SOTER客戶端過程封裝介面soter-wrapper:封裝了SOTER具體流程以及適配問題機型。sdk大小約70KB,安裝包大小增量更少;
  • 快速上手的客戶端demo,從零開始,幫你短短几行程式碼實現指紋支付;
  • 完整的客戶端文件和後臺介面文件;
  • 完整的原理剖析和快速入手。

這一切,盡在TENCENT SOTER(GitHub地址:https://github.com/Tencent/soter,點選 閱讀全文 亦可直接訪問)。

使用SOTER最快能多塊?如果你只需要做鎖屏之類對安全性要求不高的需求,只需要:

  • 新增gradle依賴

在專案的build.gradle中,新增 SOTER依賴

dependencies {    
        ...    
        compile 'com.tencent.soter:soter-wrapper:1.3.8'    
        ... 
}複製程式碼
  • 宣告許可權

在 AndroidManifest.xml中新增使用指紋許可權

<uses-permission 
android:name="android.permission.USE_FINGERPRINT"/>複製程式碼
  • 初始化

初始化過程整個應用宣告週期內只需要進行一次,用於生成基本配置和檢查裝置支援情況。你可以選擇在Application的onCreate()中,或者在使用SOTER之前進行初始化。

InitializeParam param = new InitializeParam.InitializeParamBuilder()
.setScenes(0) // 場景值常量,後續使用該常量進行金鑰生成或指紋認證
.build();
SoterWrapperApi.init(context, new 
SoterProcessCallback<SoterProcessNoExtResult>() {...}, 
param);複製程式碼
  • 準備金鑰

需要在使用指紋認證之前生成相關金鑰

SoterWrapperApi.prepareAuthKey(new
 SoterProcessCallback<SoterProcessKeyPreparationResult
>() {...},false, true, 0, null, null);複製程式碼
  • 進行指紋認證

金鑰生成完畢之後,可以使用封裝介面呼叫指紋感測器進行認證。

AuthenticationParam param = new AuthenticationParam.AuthenticationParamBuilder()
  .setScene(0)                                    
  .setContext(MainActivity.this)                                    
  .setFingerprintCanceller(mSoterFingerprintCanceller)
  .setPrefilledChallenge("test challenge")    
                                
  .setSoterFingerprintStateCallback(new 
SoterFingerprintStateCallback() {...}).build();
SoterWrapperApi.requestAuthorizeAndSign(new
SoterProcessCallback<SoterProcessAuthenticationResult>
() {...}, param);複製程式碼

當然了,如果你想要實現指紋支付、指紋登入等高安全性場景,還有一些其他工作要做,具體可以參考我們的示例程式碼(連結:https://github.com/Tencent/soter/tree/master/soter-client-demo)和安全接入文件(連結:https://github.com/Tencent/soter/wiki/%E5%AE%89%E5%85%A8%E6%8E%A5%E5%85%A5)。

SOTER(GitHub地址:https://github.com/Tencent/soter) 開源之後,已經有包括微眾銀行在內的多個應用已經接入,這些應用接入的時間均不超過一個版本。使用的場景也從指紋支付,到指紋登入、指紋解鎖。用過的,都說好。

那麼,讓我們再回顧下開頭的場景:“我們要做指紋支付,下個版本上…”,想必你已經知道怎麼做了,括弧逃~


問答

為什麼手機屏下指紋技術難以實現?

相關閱讀

作業系統指紋識別概述

移動支付安全評測:微信支付篇

微信送你一把未來的萬能鑰匙


此文已由作者授權騰訊雲+社群釋出,轉載請註明文章出處

原文連結:https://cloud.tencent.com/developer/article/1031094?fromSource=waitui


相關文章