Google Authenticator


Google Authenticator

現在越來越多的網站采用兩步驗證,實現方式可能有所區別,一般來說是 1+? (1 即 普通的用戶名和密碼, ?可能是實物如U盾、手機短信驗證碼或其他)。?的重點是它是一次性的(One-Time Password OTP, 即動態密碼). RFC提出了針對OTP的RFC4226, 后對其進行擴展形成了RFC6328協議(增加了 HMAC-based One-Time Password (HOTP)).

Google Authenticator就是基於RFC6328的實現, 該標准名稱為 Open Authentication (OATH), 與OAuth沒關系.

Google Authenticator APP支持Android, iOS, BlackBerry (WindowPhone不清楚).

APP 在線 Demo:
https://daplie.github.io/browser-authenticator/ (該Demo使用Google chat服務生成二維碼圖片, 中國可能無法顯示二維碼圖片)

APP & HOTP

Use steps

  1. 用戶在手機上安裝Google Authenticator APP
  2. 用戶認證的網站提供一個二維碼(即一個URL信息), 讓用戶掃描添加該賬號
  3. 用戶登錄后即可輸入該APP上顯示的動態密碼,網站驗證該動態密碼
    APP生成動態密碼 是獨立進行的,無需聯網. 網站驗證過程也是獨立的

二維碼或URL的格式(內容)

關鍵是secret,其他都是一些提示信息

otpauth://TYPE/issuer:user?PARAMETERS

TYPE: hotp or totp
issuer: 發行方 <和后面的參數一致>
user: 用戶賬號
Parameters:
    secret: 密鑰
    issuer: 發行方
    其他可選參數, 以及各個細節內容 參見下面Google連接
    

該URL可以將其生成一個二維碼, 用戶通過APP掃描輸入, 也可以手動輸入user和secret

例如:
otpauth://totp/xiaowei:xiaowei@gmail.com?secret=UNYFYWFE7IN6MOAW&issuer=xiaowei

Algorithm

下面的代碼采用 python3 描述

0. 生成URL上的secret

    # 首先選擇一個原始密鑰 key, 長度在
    # 然后使用base32進行編碼即可, 可以省略padding符(即后面的=)
    # 由於base32結果以%8對齊, 我們也可以截取編碼后的8n個長度的字符作為secret

1. 生成動態密碼

    # 參數:  secret, input = None
    # 通過 secret 得到 原始密鑰 key
    key = b32decode(secret) # 需要對padding符進行處理
    if input == None:
        input = int(time.time()) // 30 # input 為次數, 30為默認密碼刷新間隔值
    # 然后使用 HMAC-SHA1算法計算hash
    hsh = hmac.new(key, input, hashlib.sha1).digest()
    # 將hsh轉換成數字(默認為6位)
    i = hsh[-1] & 0xf # 以最后一個字節的后4個bits為數字,作為接下來的索引
    f = hsh[i:i+4] # 以i為索引, 取hsh中的4個字節
    n = struct.unpack('>I', f)[0] & 0x7fffffff# 將4個字節按big-endian轉換為無符號整數, 轉換時去掉最高位的符號位
    # 等價於 n = ((f[0] & 0x7f) << 24) | ((f[1] & 0xff) << 16) | ((f[2] & 0xff) << 8) | (f[3] & 0xff)
    # 將 n % 1000000 得到6位數字, 不足補零
    r = '%06d' % (n % 1000000) # r 即為 生成的動態密碼

2. 校驗動態密碼

    # 參數:  secret, n, window=1
    #                      window為時間窗口, 也就是動態密碼有效的時間期限
    
    cur_input = int(time.time()) // 30
    for i in range(cur_input-(window-1)//2, cur_input + window//2 + 1): # [cur_input-(window-1)//2, cur_input + window//2]
        d = generate_dynamic_code(secret, i)
        if d == n:
            return True
    return False

3. 備注

調試時,可以使用online qrcode將URL轉換為二維碼

Ref:

Github上一個python2實現: rentshare

Google開源的Authenticator:Google

協議:rfc6328

CSDN上的一個中文原理描述:csdn


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM