兩步驗證
大家應該對兩步驗證都熟悉吧?如蘋果有自帶的兩步驗證策略,防止用戶賬號密碼被盜而鎖定手機進行敲詐,這種例子屢見不鮮,所以蘋果都建議大家開啟兩步驗證的。
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 類的源碼邏輯參考。
如果有收獲歡迎點贊轉發,也可以留言發表你的疑問和看法。