如何生成一次性密码(OTP / HOTP)?

时间:2011-03-02 14:55:34

标签: c# security

我们决定开始研究多因素身份验证,为我们的客户发布iPhone,Android和Blackberry应用程序。

考虑Google Authenticator的一次性密码系统。

我可以通过使用基于帐户密钥加上设备序列号(或其他唯一标识符)的SALT进行散列来生成唯一的字符串

但有没有人知道如何以谷歌的方式生成一个独特的短号码?和/或是否有人有任何关于实现此类事情的文章的良好链接?

非常感谢

2 个答案:

答案 0 :(得分:10)

最后我发现这在RFC 4226中有很好的记录,关于整数转换,这可以使用按位运算shown on page 7完成,基本上它与显示的相同。回答如下。

在C#上下文中有another post on stackoverflow,如果你处于类似的位置,可能值得一读。

在C#中我基本上散列了一个时间标识符(即以秒为单位的当前时间除以30 - 得到一个对当前30秒间隔有效的长度)。然后使用我的密钥作为SALT对此进行哈希处理。

然后......

// Use a bitwise operation to get a representative binary code from the hash
// Refer section 5.4 at http://tools.ietf.org/html/rfc4226#page-7            
int offset = hashBytes[19] & 0xf;
int binaryCode = (hashBytes[offset] & 0x7f) << 24
    | (hashBytes[offset + 1] & 0xff) << 16
    | (hashBytes[offset + 2] & 0xff) << 8
    | (hashBytes[offset + 3] & 0xff);

// Generate the OTP using the binary code. As per RFC 4426 [link above] "Implementations MUST extract a 6-digit code at a minimum 
// and possibly 7 and 8-digit code"
int otp = binaryCode % (int)Math.Pow(10, 6); // where 6 is the password length

return otp.ToString().PadLeft(6, '0');

对于那些不知道的人,Google身份验证器是一个开源项目 - 您可以browse the source code here

答案 1 :(得分:6)

嗯, 不是唯一的。它只需要有相当多的熵。这意味着获得相同字符串的可能性相当低。

这样做的一种方法是使用哈希并切断一定数量的整数:

var hash = sha1(salt + device + secretKey);
var numbers = base_convert(hash, 16, 10); // Convert hex string to a integer
var key = numbers % 100000; // Limit to 5 digits (you can change this on need)

请记住将数字留空,如果它太短,则以文字0开头。