TOTP算法實現二步驗證


概念

TOTP算法(Time-based One-time Password algorithm)是一種從共享密鑰和當前時間計算一次性密碼的算法。 它已被采納為Internet工程任務組標准RFC 6238,是Initiative for Open Authentication(OATH)的基石,並被用於許多雙因素身份驗證系統。
TOTP是基於散列的消息認證碼(HMAC)的示例。 它使用加密哈希函數將密鑰與當前時間戳組合在一起以生成一次性密碼。 由於網絡延遲和不同步時鍾可能導致密碼接收者必須嘗試一系列可能的時間來進行身份驗證,因此時間戳通常以30秒的間隔增加,從而減少了潛在的搜索空間。

算法介紹:https://tools.ietf.org/html/rfc6238

算法描述

OTP基於具有時間戳計數器的OTP。
通過定義紀元(T0)的開始並以時間間隔(TI)為單位計數,將當前時間戳變為整數時間計數器(TC)。 例如:

  1. TC = floor,
  2. TOTP = HOTP(SecretKey,TC),
  3. TOTP-Value = TOTP mod 10d,其中d是一次性密碼的所需位數。
    像google auth的二步認證使用了這種方式。

totp二步認證的過程我總結了一下

  1. 生成二維碼,帶有otpauth鏈接的google地址
  2. 生成公用密鑰
  3. 返回給app,同時用戶戶和服務名也會返回,這時密鑰是被base32加密過的,app存儲,以后用這個密鑰來生成6位校驗碼
  4. 服務端同時存儲這個密鑰和用戶名,你可以把用戶名當key,把密鑰當value進行存儲
  5. app每30秒生成一個6位校驗碼,用戶使用這個碼來網站進行登陸
  6. 服務器使用存儲的密鑰+fmac算法生成6位隨機數,與客戶端傳來的數進行對比
  7. 兩個碼相等,授權成功,反之,失敗.(注意,服務端可以根據當前登陸的用戶名拿到它的密鑰,有了密鑰,再進行totp的算法生成校驗碼)

登陸的過程整理

  1. 用戶和密碼先登陸
  2. session里存儲了用戶名等信息
  3. 產生二維碼及密鑰,密鑰存儲到服務器的k/v介質里,k使用session里的用戶名,v使用剛才的密鑰
  4. 客戶使用app掃二維碼,產生新的6位數字
  5. 客戶在用戶名和密碼登陸后,進行驗證碼頁面,輸入剛才的6位數字
  6. 提交到服務端,服務端根據用戶名取出對應的密鑰,然后使用totp算法生成6位數字
  7. 如果服務端與客戶端數字相同,表示登陸成功!

totp核心算法

public static String generateTOTP(String key,
             String time,
             String returnDigits,
             String crypto){
         int codeDigits = Integer.decode(returnDigits).intValue();
         String result = null;

         // Using the counter
         // First 8 bytes are for the movingFactor
         // Compliant with base RFC 4226 (HOTP)
         while (time.length() < 16 )
             time = "0" + time;

         // Get the HEX in a Byte[]
         byte[] msg = hexStr2Bytes(time);
         byte[] k = hexStr2Bytes(key);
         byte[] hash = hmac_sha(crypto, k, msg);

         // put selected bytes into result int
         int offset = hash[hash.length - 1] & 0xf;

         int binary =
             ((hash[offset] & 0x7f) << 24) |
             ((hash[offset + 1] & 0xff) << 16) |
             ((hash[offset + 2] & 0xff) << 8) |
             (hash[offset + 3] & 0xff);

         int otp = binary % DIGITS_POWER[codeDigits];

         result = Integer.toString(otp);
         while (result.length() < codeDigits) {
             result = "0" + result;
         }
         return result;
     }


免責聲明!

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



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