日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

TOTP 介绍及基于 C# 的简单实现

發布時間:2023/12/4 C# 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TOTP 介绍及基于 C# 的简单实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

TOTP 介紹及基于 C# 的簡單實現

Intro

TOTP 是基于時間的一次性密碼生成算法,它由 RFC 6238 定義。和基于事件的一次性密碼生成算法不同 HOTP,TOTP 是基于時間的,它和 HOTP 具有如下關系:

  • TOTP = HOTP(K, T)

  • HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

  • 其中:

    • T:T = (Current Unix time - T0) / X, T0 = 0,X = 30

    • K:客戶端和服務端的共享密鑰,不同的客戶端的密鑰各不相同。

    • HOTP:該算法請參考 RFC,也可參考 理解 HMAC-Based One-Time Password Algorithm

    TOTP 算法是基于 HOTP 的,對于 HOTP 算法來說,HOTP 的輸入一致時始終輸出相同的值,而 TOTP 是基于時間來算出來的一個值,可以在一段時間內(官方推薦是30s)保證這個值是固定以實現,在一段時間內始終是同一個值,以此來達到基于時間的一次性密碼生成算法,使用下來整體還不錯,有個小問題,如果需要實現一個密碼只能驗證一次需要自己在業務邏輯里實現,只能自己實現,TOTP 只負責生成和驗證。

    C# 實現 TOTP

    實現代碼

  • using System;

  • using System.Security.Cryptography;

  • using System.Text;


  • namespace WeihanLi.Totp

  • {

  • public class Totp

  • {

  • private readonly OtpHashAlgorithm _hashAlgorithm;

  • private readonly int _codeSize;


  • public Totp() : this(OtpHashAlgorithm.SHA1, 6)

  • {

  • }


  • public Totp(OtpHashAlgorithm otpHashAlgorithm, int codeSize)

  • {

  • _hashAlgorithm = otpHashAlgorithm;


  • // valid input parameter

  • if (codeSize <= 0 || codeSize > 10)

  • {

  • throw new ArgumentOutOfRangeException(nameof(codeSize), codeSize, "length must between 1 and 9");

  • }

  • _codeSize = codeSize;

  • }


  • private static readonly Encoding Encoding = new UTF8Encoding(false, true);


  • public virtual string Compute(string securityToken) => Compute(Encoding.GetBytes(securityToken));


  • public virtual string Compute(byte[] securityToken) => Compute(securityToken, GetCurrentTimeStepNumber());


  • private string Compute(byte[] securityToken, long counter)

  • {

  • HMAC hmac;

  • switch (_hashAlgorithm)

  • {

  • case OtpHashAlgorithm.SHA1:

  • hmac = new HMACSHA1(securityToken);

  • break;


  • case OtpHashAlgorithm.SHA256:

  • hmac = new HMACSHA256(securityToken);

  • break;


  • case OtpHashAlgorithm.SHA512:

  • hmac = new HMACSHA512(securityToken);

  • break;


  • default:

  • throw new ArgumentOutOfRangeException(nameof(_hashAlgorithm), _hashAlgorithm, null);

  • }


  • using (hmac)

  • {

  • var stepBytes = BitConverter.GetBytes(counter);

  • if (BitConverter.IsLittleEndian)

  • {

  • Array.Reverse(stepBytes); // need BigEndian

  • }

  • // See https://tools.ietf.org/html/rfc4226

  • var hashResult = hmac.ComputeHash(stepBytes);


  • var offset = hashResult[hashResult.Length - 1] & 0xf;

  • var p = "";

  • for (var i = 0; i < 4; i++)

  • {

  • p += hashResult[offset + i].ToString("X2");

  • }

  • var num = Convert.ToInt64(p, 16) & 0x7FFFFFFF;


  • //var binaryCode = (hashResult[offset] & 0x7f) << 24

  • // | (hashResult[offset + 1] & 0xff) << 16

  • // | (hashResult[offset + 2] & 0xff) << 8

  • // | (hashResult[offset + 3] & 0xff);


  • return (num % (int)Math.Pow(10, _codeSize)).ToString();

  • }

  • }


  • public virtual bool Verify(string securityToken, string code) => Verify(Encoding.GetBytes(securityToken), code);


  • public virtual bool Verify(string securityToken, string code, TimeSpan timeToleration) => Verify(Encoding.GetBytes(securityToken), code, timeToleration);


  • public virtual bool Verify(byte[] securityToken, string code) => Verify(securityToken, code, TimeSpan.Zero);


  • public virtual bool Verify(byte[] securityToken, string code, TimeSpan timeToleration)

  • {

  • var futureStep = (int)(timeToleration.TotalSeconds / 30);

  • var step = GetCurrentTimeStepNumber();

  • for (int i = -futureStep; i <= futureStep; i++)

  • {

  • if (step + i < 0)

  • {

  • continue;

  • }

  • var totp = Compute(securityToken, step + i);

  • if (totp == code)

  • {

  • return true;

  • }

  • }

  • return false;

  • }


  • private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);


  • /// <summary>

  • /// timestep

  • /// 30s(Recommend)

  • /// </summary>

  • private static readonly long _timeStepTicks = TimeSpan.TicksPerSecond * 30;


  • // More info: https://tools.ietf.org/html/rfc6238#section-4

  • private static long GetCurrentTimeStepNumber()

  • {

  • var delta = DateTime.UtcNow - _unixEpoch;

  • return delta.Ticks / _timeStepTicks;

  • }

  • }

  • }

  • 使用方式:

  • var otp = new Totp(OtpHashAlgorithm.SHA1, 4); // 使用 SHA1算法,輸出4位

  • var secretKey = "12345678901234567890";

  • var output = otp.Compute(secretKey);

  • Console.WriteLine($"output: {output}");

  • Thread.Sleep(1000 * 30);

  • var verifyResult = otp.Verify(secretKey, output); // 使用默認的驗證方式,30s內有效

  • Console.WriteLine($"Verify result: {verifyResult}");

  • verifyResult = otp.Verify(secretKey, output, TimeSpan.FromSeconds(60)); // 指定可容忍的時間差,60s內有效

  • Console.WriteLine($"Verify result: {verifyResult}");

  • 輸出示例:

    Reference

    • https://tools.ietf.org/html/rfc4226

    • https://tools.ietf.org/html/rfc6238

    • http://wsfdl.com/algorithm/2016/04/05/%E7%90%86%E8%A7%A3HOTP.html

    • http://wsfdl.com/algorithm/2016/04/14/%E7%90%86%E8%A7%A3TOTP.html

    • https://www.cnblogs.com/voipman/p/6216328.html


    總結

    以上是生活随笔為你收集整理的TOTP 介绍及基于 C# 的简单实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。