我正在尝试使用可以使用Google Authenticator application生成的一次性密码。
Google Authenticator的用途
基本上,Google Authenticator实现了两种类型的密码:
谷歌验证器也可以在这里获得开源版本:code.google.com/p/google-authenticator
当前代码
我一直在寻找现有的解决方案来生成HOTP和TOTP密码,但没有找到太多。我拥有的代码是以下负责生成HOTP的代码片段:
import hmac, base64, struct, hashlib, time
def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None):
if intervals_no == None:
intervals_no = int(time.time()) // 30
key = base64.b32decode(secret)
msg = struct.pack(">Q", intervals_no)
h = hmac.new(key, msg, digest_mode).digest()
o = ord(h[19]) & 15
h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
return h
我面临的问题是,我使用上面的代码生成的密码与使用Android版Google Authenticator应用程序生成的密码不同。尽管我尝试了多个键值(前10000个,从intervals_no = 0
开始),但secret
等于GA应用程序中提供的intervals_no
。
我有一些问题
我的问题是:
总而言之:请给我任何线索,帮助我在Python代码中实现Google Authenticator身份验证。
发布于 2011-12-18 12:47:19
我想对我的问题设置一个赏金,但我已经成功地创建了解决方案。我的问题似乎与secret
密钥的值不正确有关(它必须是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
是服务器(上面的脚本)和客户端(谷歌验证器,通过在应用程序中将其作为密码提供)已知的保密值,intervals_no
是每次生成令牌后递增的数字(这可能会在服务器上通过在过去最后一次成功检查后检查一些有限数量的整数来解决)如何使用它
secret
(它必须是base64.b32decode()
的正确参数)-最好是16个字符(没有=
符号),因为它肯定适用于脚本和谷歌Authenticator.
get_hotp_token()
,如果你想在每次使用后使一次性密码无效的话。在Google Authenticator中,我提到的这类密码是基于计数器的。为了在服务器上检查它,您将需要检查intervals_no
的几个值(因为您不能保证用户由于某种原因没有生成请求之间的传递),但不能小于最后一个工作的intervals_no
值(因此,如果您希望一个令牌以30秒的间隔工作,您可能应该将其存储为somewhere).
get_totp_token()
。你必须确保两个系统都设置了正确的时间(这意味着它们在任何给定的时刻都会生成相同的Unix时间戳)。
示例
使用以下代码作为基于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个字符的长度)。
发布于 2014-04-22 21:52:48
我想要一个python脚本来生成TOTP密码。因此,我编写了python脚本。这是我的实现。我在维基百科上有这个info和一些关于HOTP和TOTP的知识来写这个脚本。
import hmac, base64, struct, hashlib, time, array
def Truncate(hmac_sha1):
"""
Truncate represents the function that converts an HMAC-SHA-1
value into an HOTP value as defined in Section 5.3.
http://tools.ietf.org/html/rfc4226#section-5.3
"""
offset = int(hmac_sha1[-1], 16)
binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff
return str(binary)
def _long_to_byte_array(long_num):
"""
helper function to convert a long number into a byte array
"""
byte_array = array.array('B')
for i in reversed(range(0, 8)):
byte_array.insert(0, long_num & 0xff)
long_num >>= 8
return byte_array
def HOTP(K, C, digits=6):
"""
HOTP accepts key K and counter C
optional digits parameter can control the response length
returns the OATH integer code with {digits} length
"""
C_bytes = _long_to_byte_array(C)
hmac_sha1 = hmac.new(key=K, msg=C_bytes, digestmod=hashlib.sha1).hexdigest()
return Truncate(hmac_sha1)[-digits:]
def TOTP(K, digits=6, window=30):
"""
TOTP is a time-based variant of HOTP.
It accepts only key K, since the counter is derived from the current time
optional digits parameter can control the response length
optional window parameter controls the time window in seconds
returns the OATH integer code with {digits} length
"""
C = long(time.time() / window)
return HOTP(K, C, digits=digits)
发布于 2021-09-27 08:12:48
通过遵循@tadeck和@Anish-Shah的正确答案,就有了一种更简单的方法来获得代码,而无需使用struct
并避免额外的导入:
""" TOTP """
import hmac
import time
def totp(key: bytes):
""" Calculate TOTP using time and key """
now = int(time.time() // 30)
msg = now.to_bytes(8, "big")
digest = hmac.new(key, msg, "sha1").digest()
offset = digest[19] & 0xF
code = digest[offset : offset + 4]
code = int.from_bytes(code, "big") & 0x7FFFFFFF
code = code % 1000000
return "{:06d}".format(code)
这适用于Python 3。
您可以通过调用totp(key)
来获取当前的TOTP代码,其中的“bytes
”是一个密钥(通常是32进制解码的密钥)。
https://stackoverflow.com/questions/8529265
复制相似问题