兩步驗證
大家應該對兩步驗證都熟悉吧?如蘋果有自帶的兩步驗證政策,防止使用者賬号密碼被盜而鎖定手機進行敲詐,這種例子屢見不鮮,是以蘋果都建議大家開啟兩步驗證的。
Google 的身份驗證器一般也是用于登入進行兩步驗證,和蘋果的兩步驗證是同樣的道理。隻不過 Google 的身份驗證器用得更多更廣泛,如 GitHub 的兩步驗證都是基于 Google 身份驗證器。
Google Authenticator 簡介
Google Authenticator 身份驗證器是一款基于時間與哈希的一次性密碼算法的兩步驗證軟體令牌,使用者需要下載下傳手機 APP(Authenticator),該手機 APP 與網站進行綁定,當網站驗證完使用者名和密碼之後會驗證此 APP 上對應生成的 6 位驗證碼數字,驗證通過則成功登入,否則登入失敗。
Google Authenticator 使用
我們來看下 Github 上的使用 Google 身份驗證器開啟兩步驗證的應用。
如圖所示,預設 Github 是沒有開啟兩步驗證的,點選設定按鈕進行設定。

Github 提供了基于 APP (谷歌身份驗證器)和短信驗證碼兩種兩步驗證的方式,我們選擇第一種谷歌身份驗證器。

進入第一種驗證模式,接下來展示了一堆的恢複碼,用來當 APP 驗證器不能工作的緊急情況使用。把它們儲存起來,然後點選下一步。

這個就是身份驗證器的關鍵了,下載下傳 Google 的
Authenticator
APP,然後掃描這個二維碼進行綁定。

綁定之後,APP Github 子產品下面會顯示一個 6 位的驗證碼,把它輸入到上面那個框裡面就行了。

如下圖所示,已經成功開啟兩步驗證了。

接下來我們退出 Github 再重新登入,頁面就會提示要輸入 Google 的身份驗證器驗證碼了,如果 APP 不能正常工作,最下方還能通過之前儲存下來的恢複碼進行登入。

好了,Google Authenticator 使用就到這裡,那它是如何工作的,它是什麼原理呢?我們的網站、APP 如何接入 Google Authenticator,接下來我們一一拉開謎底。
Google Authenticator 工作流程
實際上 Google Authenticator 采用的是 TOTP 算法(Time-Based One-Time Password,即基于時間的一次性密碼),其核心内容包括以下三點。
1、安全密鑰
是用戶端和服務端約定的安全密鑰,也是手機端 APP 身份驗證器綁定(手機端通過掃描或者手輸安全密鑰進行綁定)和驗證碼的驗證都需要的一個唯一的安全密鑰,該密鑰由加密算法生成,并最後由 Base32 編碼而成。
2、驗證時間
Google 選擇了 30 秒作為時間片,T的數值為 從Unix epoch(1970年1月1日 00:00:00)來經曆的 30 秒的個數,是以在 Google Authenticator 中我們可以看見驗證碼每個 30 秒就會重新整理一次。
更詳細原理參考:
https://blog.seetee.me/post/2011/google-two-step-verification/
3、簽署算法
Google 使用的是 HMAC-SHA1 算法,全稱是:Hash-based message authentication code(哈希運算消息認證碼),它是以一個密鑰和一個消息為輸入,生成一個消息摘要作為輸出,這裡以 SHA1 算法作為消息輸入。
使用 HMAC 算法是因為隻有使用者本身知道正确的輸入密鑰,是以會得到唯一的輸出,其算法可以簡單表示為:
hmac = SHA1(secret + SHA1(secret + input))
事實上,TOTP 是 HMAC-OTP(基于HMAC的一次密碼生成)的超集,差別是 TOTP 是以目前時間作為輸入,而HMAC-OTP 則是以自增電腦作為輸入,該計數器使用時需要進行同步。
Google Authenticator 實戰
知道上面的原理,我們就可以來應用實戰了。
/**
* 微信公衆号:Java技術棧
*/
public class AuthTest {
@Test
public void genSecretTest() {
String secret = GoogleAuthenticator.generateSecretKey();
String qrcode = GoogleAuthenticator.getQRBarcodeURL("Java技術棧", "javastack.cn", secret);
System.out.println("二維碼位址:" + qrcode);
System.out.println("密鑰:" + secret);
}
@Test
public void verifyTest() {
String secret = "ZJTAQGLVOZ7ATWH2";
long code = 956235;
GoogleAuthenticator ga = new GoogleAuthenticator();
boolean r = ga.verifCode(secret, code);
System.out.println("是否正确:" + r);
}
}
第一個方法是生成密鑰和一個掃描二維碼綁定的URL。
第二個方法是根據密鑰和驗證碼進行驗證。
這裡僅提供一下 GoogleAuthenticator 類的源碼邏輯參考。
http://awtqty-zhang.iteye.com/blog/1986275