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

Jos*_*osh 6 c# hmac one-time-password

我试图围绕生成一个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.
Run Code Online (Sandbox Code Playgroud)

然后我们有算法来生成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))
Run Code Online (Sandbox Code Playgroud)

然后,我们将Truncate定义为

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
Run Code Online (Sandbox Code Playgroud)

然后为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) ;
Run Code Online (Sandbox Code Playgroud)

我宁愿亏本尝试将其转换为有用的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);
Run Code Online (Sandbox Code Playgroud)

我只是不确定如何从那里,到上述算法中提出的6位数.

Phi*_*eck 3

你这里有两个问题:

  1. 如果您生成字母数字,则不符合 RFC - 此时,您可以简单地获取任意 N 个字节并将它们转换为十六进制字符串并获得字母数字。或者,如果您需要 az 和 0-9,请将它们转换为基数 36 。RFC 的第 5.4 节为您提供了设置Digit参数的标准 HOTP 计算(请注意,它Digit是与CK和 一起的参数T)。如果您选择忽略此部分,则无需转换代码 - 只需使用您想要的内容即可。

  2. 您的“结果”字节数组的过期时间只是简单地填充在散列后的前 8 个字节中。如果您截断为 6 位字母数字后没有收集到这些内容以及部分哈希值,那么它可能根本不会被计算。“伪造”或重播也很容易 - 将秘密散列一次,然后在其前面加上您想要的任何标记 - 这并不是真正的一次性密码。请注意,CRFC 中的参数旨在满足到期窗口的要求,并且应在计算哈希码之前将其添加到输入中。