首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Google Authenticator在Python中的实现

Google Authenticator在Python中的实现
EN

Stack Overflow用户
提问于 2011-12-16 10:57:14
回答 3查看 56.6K关注 0票数 116

我正在尝试使用可以使用Google Authenticator application生成的一次性密码。

Google Authenticator的用途

基本上,Google Authenticator实现了两种类型的密码:

  • HOTP -基于and
  • TOTP的一次性密码,这意味着密码在每次调用时都会更改,符合RFC4226 -基于HMAC时间的一次性密码,每30秒(据我所知)更改一次。

谷歌验证器也可以在这里获得开源版本:code.google.com/p/google-authenticator

当前代码

我一直在寻找现有的解决方案来生成HOTP和TOTP密码,但没有找到太多。我拥有的代码是以下负责生成HOTP的代码片段:

代码语言:javascript
复制
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

我有一些问题

我的问题是:

  1. 我做错了什么?
  2. 如何在Python语言中生成HOTP和/或TOTP?
  3. 是否有用于此目的的现有Python库?

总而言之:请给我任何线索,帮助我在Python代码中实现Google Authenticator身份验证。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-12-18 12:47:19

我想对我的问题设置一个赏金,但我已经成功地创建了解决方案。我的问题似乎与secret密钥的值不正确有关(它必须是base64.b32decode()函数的正确参数)。

下面我将发布完整的工作解决方案,并解释如何使用它。

代码

下面的代码就足够了。我还把它作为一个单独的模块上传到了GitHub上,这个模块名为onetimepass (可以在这里找到:https://github.com/tadeck/onetimepass)。

代码语言:javascript
复制
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是每次生成令牌后递增的数字(这可能会在服务器上通过在过去最后一次成功检查后检查一些有限数量的整数来解决)

如何使用它

Google

  • 生成secret (它必须是base64.b32decode()的正确参数)-最好是16个字符(没有=符号),因为它肯定适用于脚本和谷歌Authenticator.

  • Use get_hotp_token(),如果你想在每次使用后使一次性密码无效的话。在Google Authenticator中,我提到的这类密码是基于计数器的。为了在服务器上检查它,您将需要检查intervals_no的几个值(因为您不能保证用户由于某种原因没有生成请求之间的传递),但不能小于最后一个工作的intervals_no值(因此,如果您希望一个令牌以30秒的间隔工作,您可能应该将其存储为somewhere).

  • Use get_totp_token()。你必须确保两个系统都设置了正确的时间(这意味着它们在任何给定的时刻都会生成相同的Unix时间戳)。

  • 确保保护自己免受暴力破解攻击。如果使用基于时间的密码,那么在不到30秒的时间内尝试1000000个值将有100%的机会猜到密码。在基于HMAC的密码(HOTP)的情况下,情况似乎更糟。

示例

使用以下代码作为基于HMAC的一次性密码时:

代码语言:javascript
复制
secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
    print i, get_hotp_token(secret, intervals_no=i)

您将得到以下结果:

代码语言:javascript
复制
1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710

这与Google Authenticator应用程序生成的令牌相对应(如果短于6个符号,则应用程序会在开头添加零,以达到6个字符的长度)。

票数 165
EN

Stack Overflow用户

发布于 2014-04-22 21:52:48

我想要一个python脚本来生成TOTP密码。因此,我编写了python脚本。这是我的实现。我在维基百科上有这个info和一些关于HOTP和TOTP的知识来写这个脚本。

代码语言:javascript
复制
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)
票数 7
EN

Stack Overflow用户

发布于 2021-09-27 08:12:48

通过遵循@tadeck和@Anish-Shah的正确答案,就有了一种更简单的方法来获得代码,而无需使用struct并避免额外的导入:

代码语言:javascript
复制
""" 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进制解码的密钥)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8529265

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档