我试图将我的大脑包裹着产生6位数字/角色敏感的一次性密码。

我的来源是 http://tools.ietf.org/html/rfc4226#section-5

首先是参数的定义

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位HOTP提供一个示例

The following code example describes the extraction of a dynamic
binary code given that hmac_result is a byte array with the HMAC-
SHA-1 result:

    int offset   =  hmac_result[19] & 0xf ;
    int bin_code = (hmac_result[offset]  & 0x7f) << 24
       | (hmac_result[offset+1] & 0xff) << 16
       | (hmac_result[offset+2] & 0xff) <<  8
       | (hmac_result[offset+3] & 0xff) ;

我宁愿将其转换为有用的C#代码来生成一次密码。我已经有用于创建到期HMAC的代码,如下所示:

byte[] hashBytes = alg.ComputeHash(Encoding.UTF8.GetBytes(input));
byte[] result = new byte[8 + hashBytes.Length];

hashBytes.CopyTo(result, 8);
BitConverter.GetBytes(expireDate.Ticks).CopyTo(result, 0);

我只是不确定如何从上述算法中提出的6位数字。

有帮助吗?

解决方案

您在这里有两个问题:

  1. 如果您正在生成alpha-numeric,则不符合RFC - 此时,您可以简单地采用任何n个字节,然后将它们变成十六进制字符串并获得alpha-numeric。或者, 将它们转换为基础36 如果您想要AZ和0-9。 RFC的第5.4节为您提供了一套标准的热门计算 Digit 参数(请注意 Digit 是一个参数 C, K, , 和 T)。如果您选择忽略本节,则无需转换代码 - 只需使用所需的内容即可。

  2. 您的“结果”字节数组的到期时间仅在哈希后的前8个字节中塞入。如果您对6位字母数字的截断不会与一部分哈希收集它们,则可能根本无法计算出来。 “伪造”或重播也很容易 - 哈希秘密一次,然后将您想要的任何滴答物放在它的前面 - 实际上不是一个一次性密码。请注意该参数 C 在RFC中旨在满足到期的窗口,应添加到输入中 之前 计算哈希代码。

其他提示

对于有兴趣的任何人,我确实找到了一种将到期纳入我的一次密码的方法。该方法是将创建的时间降至分钟(忽略秒,毫秒等)。拥有该值后,将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

我到期的HOTP从我的数字HOTP延伸,该数字HOTP具有一种静态验证方法,该方法检查长度,确保其数字,如果使用了校验和,则验证了校验和,最后将HOTP与生成的HOTP进行了比较。

唯一的缺点是,每次您验证到期的HOTP时,更糟糕的情况是检查n + 1 HOTP值,其中n在几分钟内为n的寿命。

文档中的Java代码示例概述了RFC 4226是C#的非常简单的移动。我真正要付出任何努力重写的唯一作品是哈希方法。

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();
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top