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

2021-06-07 08:04

阅读:347

标签:rip   name   man   ati   htm   mat   poc   ext   src   

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  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  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 
        /// timestep
        /// 30s(Recommend)
        /// 
        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#的简单实现

标签:rip   name   man   ati   htm   mat   poc   ext   src   

原文地址:https://www.cnblogs.com/weihanli/p/simple-implement-on-totp-via-csharp.html


评论


亲,登录后才可以留言!