我想为我的问题设置赏金,但我已经成功地创建了解决方案。我的问题似乎与密钥的值不正确(它必须是正确的参数base64.b32decode()函数)。
下面我发布完整的工作解决方案与解释如何使用它。
码
下面的代码就足够了。我也上传到GitHub作为单独的模块称为onetimepass(可在这里:https://github.com/tadeck/onetimepass)。
import hmac, base64, struct, hashlib, time
def get_hotp_token(secret, intervals_no):
key = base64.b32decode(secret, True)
msg = struct.pack(">Q", intervals_no)
h = hmac.new(key, msg, hashlib.sha1).digest()
o = ord(h[19]) & 15
h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
return h
def get_totp_token(secret):
return get_hotp_token(secret, intervals_no=int(time.time())//30)
它有两个功能:
> get_hotp_token()生成一次性令牌(单次使用后应该失效),
> get_totp_token()基于时间(以30秒为间隔更改)生成令牌,
参数
当涉及到参数:
> secret是服务器(上述脚本)和客户端(Google Authenticator,通过在应用程序中作为密码提供)知道的秘密值,
> intervals_no是每次生成令牌后递增的数字(这应该可能在服务器上通过在过去的最后一个成功的检查之后检查一些有限数目的整数来解决)
如何使用它
>生成秘密(它必须是base64.b32decode()的正确参数) – 最好是16-char(no = signs),因为它确实适用于脚本和Google身份验证器。
>如果您希望一次性密码在每次使用后失效,请使用get_hotp_token()。在Google Authenticator这种类型的密码,我提到的基于计数器。为了在服务器上检查它,你需要检查interval_no的几个值(因为你没有保证,用户没有生成的请求之间的通过某种原因),但不小于最后一个工作的interval_no值(因此你应该存储它在某个地方)。
>如果您希望令牌以30秒的间隔工作,请使用get_totp_token()。你必须确保两个系统都有正确的时间设置(意味着它们在任何给定的时刻都生成相同的Unix时间戳)。
>确保保护自己免受暴力攻击。如果使用基于时间的密码,则在少于30秒内尝试1000000个值将提供100%的猜测密码的机会。在基于HMAC的passowrds(HOTPs)的情况下,它似乎更糟。
例
当使用以下代码一次性基于HMAC的密码时:
secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
print i, get_hotp_token(secret, intervals_no=i)
你会得到以下结果:
1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710
这是对应于由Google Authenticator应用程序生成的令牌(除非短于6个符号,应用程序向开头添加零以达到6个字符的长度)。