中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

java和c#里的TOTP統一算法

基礎說明

本文根據 RFC4226 和 RFC6238 文檔,詳細的介紹 HOTP 和 TOTP 算法的原理和實現。
兩步驗證已經被廣泛應用于各種互聯網應用當中,用來提供安全性。對于如何使用兩步驗證,大家并不陌生,無非是開啟兩步驗證,然后出現一個二維碼,使用支持兩步驗證的移動應用比如 Google Authenticator 或者 LassPass Authenticator 掃一下二維碼。這時候應用會出現一個6位數的一次性密碼,首次需要輸入驗證從而完成開啟過程。以后在登陸的時候,除了輸入用戶名和密碼外,還需要把當前的移動應用上顯示的6位數編碼輸入才能完成登陸。
這個(ge)過程(cheng)的(de)(de)(de)背后主要由兩個(ge)算法(fa)來支撐:HOTP 和(he) TOTP。也分別對應著兩份(fen) RFC 協議(yi)(yi) RFC4266 和(he) RFC6238。前者(zhe)是 HOTP 的(de)(de)(de)標準(zhun),后者(zhe)是 TOTP 的(de)(de)(de)標準(zhun)。本文(wen)將使用圖(tu)文(wen)并(bing)茂的(de)(de)(de)方式詳細介紹 HOTP 和(he) TOTP 的(de)(de)(de)算法(fa)原理,并(bing)在最后分析其安全性。當然所有內(nei)容都是基(ji)于(yu)協議(yi)(yi)的(de)(de)(de),通過自己的(de)(de)(de)理解更加直觀的(de)(de)(de)表達出(chu)來。

為(wei)了確(que)保(bao)在不同語言下生成相同的(de) TOTP 結果(guo),你(ni)需(xu)要確(que)保(bao)使用(yong)相同的(de)密鑰和相同的(de)時間戳步長。同時,你(ni)還需(xu)要確(que)保(bao)使用(yong)相同的(de)哈希算法(fa)(通常(chang)是 HMAC-SHA1 或 HMAC-SHA256)。

參數解釋

  • base32Key: 這是生成totp數字時的共享密鑰
  • timeStep: 這是時間步長,作用是每隔多長時間(秒),你的totp數字變化一次,即一個totp數字的失效時間
  • digits: 這是生成多少位的totp數字
  • HMAC-SHA1: 是一種基于哈希函數的消息認證碼算法,用于保護數據完整性和身份驗證

HMAC-SHA1

HMAC-SHA1(Hash-based Message Authentication Code with SHA-1)是一種基于哈希(xi)(xi)函數(shu)(shu)的消息(xi)認證碼算法,用于保護(hu)數(shu)(shu)據(ju)完整性和身份驗證。它結合了兩(liang)個主要的技術:哈希(xi)(xi)函數(shu)(shu)(SHA-1)和密鑰(Key)。

下面是 HMAC-SHA1 算法的工作(zuo)原理和說明:

  1. 輸入數據:HMAC-SHA1 接受兩個(ge)輸入:消息數(shu)據(Message)和(he)密鑰(Key)。消息數(shu)據可(ke)以是(shi)任意長度的(de)二進(jin)制(zhi)數(shu)據。

  2. 密鑰填充:如(ru)果密鑰的長(chang)度小(xiao)(xiao)于哈希函(han)數(shu)的塊大小(xiao)(xiao),HMAC-SHA1 會將密鑰填充到相應的塊大小(xiao)(xiao),通常使用 0x00 字節(jie)。

  3. 內部填充:將密鑰與常數 0x36 做異或(huo)操作,然后將結果(guo)與消息數據(ju)連接起(qi)來。

  4. 哈希計算:對(dui)連接(jie)后的數據進行 SHA-1 哈希計算(suan)。SHA-1 生成一個(ge)固定長(chang)度(160位或20字節)的哈希值。

  5. 外部填充:將密鑰與常數 0x5C 做(zuo)異或操作,然后將結(jie)果與內部哈(ha)希值連接起來。

  6. 二次哈希計算:對連接(jie)后(hou)的(de)數據進行 SHA-1 哈(ha)希計算。這一(yi)次的(de)哈(ha)希計算包括了內部(bu)哈(ha)希值(zhi)和密鑰(yao)。

  7. 結果:HMAC-SHA1 的(de)最終結果是SHA-1 哈希的(de)輸出。

HMAC-SHA1 的主要(yao)目的是確(que)保數據的完整性(xing)和(he)(he)身(shen)份(fen)驗證(zheng)。由于它需要(yao)密(mi)鑰(yao),因此只(zhi)有知道密(mi)鑰(yao)的實體才(cai)能生成正確(que)的 HMAC 值。這使得 HMAC-SHA1 在加密(mi)通(tong)信和(he)(he)身(shen)份(fen)驗證(zheng)中非(fei)常有用(yong),例如在數字簽(qian)名、認證(zheng)協議(如OAuth)、以及一次性(xing)密(mi)碼(ma)算(suan)法(fa)(如TOTP和(he)(he)HOTP)中廣泛使用(yong)。

需要注(zhu)意的(de)(de)是,SHA-1 已經不再(zai)被視(shi)為安(an)全的(de)(de)哈(ha)希算(suan)法,因為它存在碰(peng)撞漏洞(dong)。因此,安(an)全敏感的(de)(de)應(ying)用程序應(ying)該使用更強(qiang)大的(de)(de)哈(ha)希算(suan)法,如(ru)SHA-256或(huo)SHA-3,來代替 SHA-1。如(ru)果可能,也(ye)應(ying)該使用更安(an)全的(de)(de) HMAC 變種,如(ru)HMAC-SHA-256。

TOTP的生成過程

  1. 服務器和客戶端都知道共享的密鑰。
  2. 客戶端獲取當前時間戳,通常是以秒為單位。
  3. 客戶端將當前時間戳除以時間步長并取整,以獲得一個時間窗口(Time Window)的序號。
  4. 客戶端使用哈希函數(如HMAC-SHA1)將密鑰和時間窗口的序號作為輸入來生成哈希值。
  5. 從哈希值中提取指定的位數作為一次性密碼,通常是6位數字。
  6. 客戶端將生成的一次性密碼顯示給用戶,用戶輸入該密碼進行身份驗證。
  7. 服務器使用相同的密鑰和時間戳計算一次性密碼,以驗證用戶輸入的密碼是否匹配。

TOTP的(de)(de)關(guan)鍵之處在(zai)于,只(zhi)有在(zai)相同的(de)(de)時(shi)間窗口內才能生成相同的(de)(de)密碼(ma)(ma),因(yin)此(ci)(ci)它能夠提供一定的(de)(de)安全性,即使密鑰泄露,攻擊者(zhe)也只(zhi)有在(zai)短時(shi)間內才能使用密碼(ma)(ma)。另外(wai),TOTP密碼(ma)(ma)的(de)(de)生成依賴于共享密鑰和時(shi)間,因(yin)此(ci)(ci)需要客戶端(duan)和服務器之間的(de)(de)時(shi)間同步。

核心代碼

以下是一個 Java 和(he) C# 中可(ke)以生成相同 TOTP 結果(guo)的示(shi)例代(dai)碼,使(shi)用的是 HMAC-SHA1 哈希算法和(he) Joda-Time 庫來處(chu)理時間(jian):

Java 示例:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base32;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

public class TOTPGenerator {
    public static String generateTOTP(String base32Key, int timeStep, int digits) throws Exception {
        long counter = (System.currentTimeMillis() / 1000) / timeStep;
        byte[] key = new Base32().decode(base32Key);
        
        SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(secretKey);
        
        byte[] counterBytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            counterBytes[7 - i] = (byte) (counter >> (8 * i));
        }
        
        byte[] hash = mac.doFinal(counterBytes);
        int offset = hash[hash.length - 1] & 0x0F;
        int binary = ((hash[offset] & 0x7F) << 24 | (hash[offset + 1] & 0xFF) << 16 | (hash[offset + 2] & 0xFF) << 8 | (hash[offset + 3] & 0xFF));
        
        int otp = binary % (int) Math.pow(10, digits);
        return String.format("%0" + digits + "d", otp);
    }
}

C# 示例:

using System;
using System.Security.Cryptography;
using System.Text;

public class TOTPGenerator
{
    public static string GenerateTOTP(string base32Key, int timeStep, int digits)
    {
        long counter = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds / timeStep;
        byte[] key = Base32Decode(base32Key);

        using (HMACSHA1 hmac = new HMACSHA1(key))
        {
            byte[] counterBytes = BitConverter.GetBytes(counter);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(counterBytes);
            }

            byte[] hash = hmac.ComputeHash(counterBytes);
            int offset = hash[hash.Length - 1] & 0x0F;
            int binary = (hash[offset] & 0x7F) << 24 | (hash[offset + 1] & 0xFF) << 16 | (hash[offset + 2] & 0xFF) << 8 | (hash[offset + 3] & 0xFF);
            
            int otp = binary % (int)Math.Pow(10, digits);
            return otp.ToString($"D{digits}");
        }
    }

    private static byte[] Base32Decode(string base32)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
        var bits = base32.ToUpper().ToCharArray().Select(c => Convert.ToString(chars.IndexOf(c), 2).PadLeft(5, '0')).Aggregate((a, b) => a + b);
        return Enumerable.Range(0, bits.Length / 8).Select(i => Convert.ToByte(bits.Substring(i * 8, 8), 2)).ToArray();
    }
}

這兩個示例使用了相同的密鑰(base32Key)、時間戳步長(timeStep)和位數(digits),并使用相(xiang)(xiang)同的(de) HMAC-SHA1 哈希算法來生成 TOTP。確保在實際應(ying)用中提(ti)供(gong)相(xiang)(xiang)同的(de)參數值,你將(jiang)能夠(gou)生成相(xiang)(xiang)同的(de) TOTP 結果。

測試代碼

// C#
string totp = GenerateTOTP("pkulaw", 30, 8);
Console.WriteLine("Current TOTP:" + totp);
// 結果:30396996

// java
String totp=generateTOTP("pkulaw", 30, 8);
System.out.println(totp);
// 結果:30396996

上面的兩種語言的測試代(dai)碼,在30秒之(zhi)內(一般使用UTC時間(jian)計算), 產生的totp碼是相同的。

posted @ 2023-10-16 10:04  張占嶺  閱讀(947)  評論(0)    收藏  舉報