C#中基于HMAC的一次性密码(RFC 4226-HOTP)
我正试图绞尽脑汁生成一个6位数/字符的非大小写敏感过期一次性密码 我的消息来源是 首先是参数的定义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
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计算(请注意,数字
与C
、K
和T
是一个参数)。如果您选择忽略此部分,则不需要转换代码,只需使用您想要的代码即可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();
}
}