C#   发布时间:2022-04-03  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了TOTP 介绍及基于C#的简单实现大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

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

Intro

TOTP 是基于时间的一次性密码生成算法,它由 RFC 6238 定义。和基于事件的一次性密码生成算法不同 HOTP,TOTP 是基于时间的,它和 HOTP 具有如下关系:

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

其中:

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)R_47_11845@ath.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,TimeSpan.Zero);

        public virtual bool Verify(byte[] securityToken,TimeSpan timeToleration)
        {
            var futureStep = (int)(timeToleration.@R_376_10586@lSeconds / 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 datetiR_47_11845@e _unixEpoch = new datetiR_47_11845@e(1970,1,datetiR_47_11845@eKind.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 = datetiR_47_11845@e.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}");

输出例:

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

Reference

大佬总结

以上是大佬教程为你收集整理的TOTP 介绍及基于C#的简单实现全部内容,希望文章能够帮你解决TOTP 介绍及基于C#的简单实现所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: