Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#中基于HMAC的一次性密码(RFC 4226-HOTP)_C#_Hmac_One Time Password - Fatal编程技术网

C#中基于HMAC的一次性密码(RFC 4226-HOTP)

C#中基于HMAC的一次性密码(RFC 4226-HOTP),c#,hmac,one-time-password,C#,Hmac,One Time Password,我正试图绞尽脑汁生成一个6位数/字符的非大小写敏感过期一次性密码 我的消息来源是 首先是参数的定义 C 8-byte counter value, the moving factor. This counter MUST be synchronized between the HOTP generator (client) and the HOTP validator (server). K shared secret between cl

我正试图绞尽脑汁生成一个6位数/字符的非大小写敏感过期一次性密码

我的消息来源是

首先是参数的定义

C       8-byte counter value, the moving factor.  This counter
       MUST be synchronized between the HOTP generator (client)
       and the HOTP validator (server).

K       shared secret between client and server; each HOTP
       generator has a different and unique secret K.

T       throttling parameter: the server will refuse connections
       from a user after T unsuccessful authentication attempts.
然后我们有了生成HOTP的算法

As the output of the HMAC-SHA-1 calculation is 160 bits, we must
   truncate this value to something that can be easily entered by a
   user.

                   HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
然后,我们将截断定义为

String = String[0]...String[19]
 Let OffsetBits be the low-order 4 bits of String[19]
 Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15
 Let P = String[OffSet]...String[OffSet+3]
 Return the Last 31 bits of P

我只是不知道如何从这一点到上述算法中建议的6位数。

这里有两个问题:

  • 如果您生成的是字母数字,那么您就不符合RFC——此时,您可以简单地获取任意N个字节,并将它们转换为十六进制字符串,然后获取字母数字。或者,如果你想要a-z和0-9。RFC的第5.4节为您提供了一组
    数字
    参数的标准HOTP计算(请注意,
    数字
    C
    K
    T
    是一个参数)。如果您选择忽略此部分,则不需要转换代码,只需使用您想要的代码即可

  • “result”字节数组在散列后的前8个字节中只填充了过期时间。如果您对6位字母数字的截断没有收集这些数据以及散列的一部分,那么也可能根本不进行计算。“伪造”或重播也很容易——将秘密散列一次,然后在它前面加上你想要的任何记号——而不是一次性密码。请注意,RFC中的参数
    C
    用于完成到期窗口,并应在计算哈希代码之前添加到输入中


  • 对于任何感兴趣的人,我确实找到了一种方法,将过期设置到我的一次性密码中。方法是使用创建的时间,精确到分钟(忽略秒、毫秒等)。一旦你有了这个值,就用DateTime的记号作为你的计数器,或者变量C

    otpLifespan
    是我的HOTP寿命,以分钟为单位

    DateTime current = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 
        DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, 0);
    
    for (int x = 0; x <= otpLifespan; x++)
    {
        var result = NumericHOTP.Validate(hotp, key, 
            current.AddMinutes(-1 * x).Ticks);
    
        //return valid state if validation succeeded
    
        //return invalid state if the passed in value is invalid 
        //  (length, non-numeric, checksum invalid)
    }
    
    //return expired state
    

    我希望这能帮助其他试图生成一次性密码的人。

    这段代码应该满足您的要求:

      public class UniqueId
    {
        public static string GetUniqueKey()
        {
            int maxSize = 6; // whatever length you want
            char[] chars = new char[62];
            string a;
            a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
               char[] chars = new char[a.Length];
            chars = a.ToCharArray();
            int size = maxSize;
            byte[] data = new byte[1];
            RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
            crypto.GetNonZeroBytes(data);
            size = maxSize;
            data = new byte[size];
            crypto.GetNonZeroBytes(data);
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            { result.Append(chars[b % (chars.Length - 1)]); }
            return result.ToString();
        }
    }
    

    我相信C是一个日期戳,K是我已经分配给每个用户帐户的密钥。至于如何正确地对它们进行散列,然后将其截断为6位,这是我感到困惑的地方。附录C提供了一个Java参考实现,该实现应该可以轻松地转换为C#。确实如此,但它只生成一个数字HOTP。我真的想要一个字母数字的HOTP。在深入研究之后,我不得不放弃一个即将到期的OTP的想法。该算法使用了一个计数器——也就是说,它被设计为在硬件设备(如加密狗/手机)和验证服务器上独立工作。这样,如果计数器是相同的,则始终生成相同的OTP——因为没有办法将数据拉回来,就像在即将到期的HMAC中一样。但你在这两方面都是对的。为了让它过期,我需要做的不仅仅是设置这个算法,如果我想要一个字母数字的OTP,那么我需要一个不同的算法。看起来不错。但是
    char[]chars=新字符[a.Length]给出错误,因为已在作用域中定义了字符。
    
    DateTime current = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 
        DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, 0);
    
    for (int x = 0; x <= otpLifespan; x++)
    {
        var result = NumericHOTP.Validate(hotp, key, 
            current.AddMinutes(-1 * x).Ticks);
    
        //return valid state if validation succeeded
    
        //return invalid state if the passed in value is invalid 
        //  (length, non-numeric, checksum invalid)
    }
    
    //return expired state
    
    private static byte[] HashHMACSHA1(byte[] keyBytes, byte[] text)
    {
        HMAC alg = new HMACSHA1(keyBytes);
    
        return alg.ComputeHash(text);
    }
    
      public class UniqueId
    {
        public static string GetUniqueKey()
        {
            int maxSize = 6; // whatever length you want
            char[] chars = new char[62];
            string a;
            a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
               char[] chars = new char[a.Length];
            chars = a.ToCharArray();
            int size = maxSize;
            byte[] data = new byte[1];
            RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
            crypto.GetNonZeroBytes(data);
            size = maxSize;
            data = new byte[size];
            crypto.GetNonZeroBytes(data);
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            { result.Append(chars[b % (chars.Length - 1)]); }
            return result.ToString();
        }
    }