Python中googleauthenticator的实现

Python中googleauthenticator的实现,python,security,authentication,one-time-password,google-authenticator,Python,Security,Authentication,One Time Password,Google Authenticator,我试图使用一次性密码,可以生成使用 谷歌验证器做什么 基本上,Google Authenticator实现两种类型的密码: HOTP-基于HMAC的一次性密码,这意味着密码会随着每次通话而更改,以符合和 TOTP基于时间的一次性密码,每30秒更改一次(据我所知) Google Authenticator也可以在以下位置以开源形式提供: 现行代码 我正在寻找现有的解决方案来生成HOTP和TOTP密码,但没有找到多少。我拥有的代码是负责生成HOTP的以下代码段: import hmac, bas

我试图使用一次性密码,可以生成使用

谷歌验证器做什么 基本上,Google Authenticator实现两种类型的密码:

  • HOTP-基于HMAC的一次性密码,这意味着密码会随着每次通话而更改,以符合和
  • TOTP基于时间的一次性密码,每30秒更改一次(据我所知)
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应用程序生成的密码不同。尽管我尝试了多个
间隔\u no
值(确切地说是前10000个,以
间隔\u no=0
开始),但
secret
等于GA应用程序中提供的密钥

我有问题 我的问题是:

  • 我做错了什么
  • 如何在Python中生成HOTP和/或TOTP
  • 是否有用于此目的的现有Python库

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

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

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

    代码 下面的代码就足够了。我还将其作为名为onetimepass的单独模块上传到GitHub(可在此处获得:)

    导入hmac、base64、struct、hashlib、time
    def get_hotp_令牌(秘密,间隔号):
    key=base64.b32密码(secret,True)
    msg=struct.pack(“>Q”,区间号)
    h=hmac.new(key,msg,hashlib.sha1).digest()
    o=ord(h[19])&15
    h=(结构解包(“>I”,h[o:o+4])[0]&0x7FFFFF)%1000000
    返回h
    def get_totp_令牌(机密):
    返回get\u hotp\u令牌(secret,interval\u no=int(time.time())//30)
    
    它有两个功能:

    • get_hotp_token()
      生成一次性令牌(一次性使用后应失效)
    • get\u totp\u token()
    参数 关于参数:

    • secret
      是服务器(上面的脚本)和客户端(Google Authenticator,通过在应用程序中提供密码)已知的秘密值
    • interval\u no
      是每次生成令牌后增加的数字(这可能应该在服务器上通过在过去检查最后一个成功的整数后检查有限数量的整数来解决)
    如何使用它
  • 生成
    secret
    (它必须是
    base64.b32decode()
    的正确参数)-最好是16个字符(无
    =
    符号),因为它肯定适用于脚本和Google Authenticator
  • 如果希望一次性密码在每次使用后失效,请使用
    get\u hotp\u token()
    。在Google Authenticator中,我提到的这类密码是基于计数器的。要在服务器上进行检查,您需要检查
    间隔\u no
    的多个值(因为您无法保证用户出于某种原因没有生成请求之间的传递),但不能小于上次工作的
    间隔\u no
    值(因此您可能应该将其存储在某个位置)
  • 如果希望令牌以30秒的间隔工作,请使用
    get\u totp\u token()
    。您必须确保两个系统都设置了正确的时间(这意味着它们在任何给定时刻都生成相同的Unix时间戳)
  • 确保保护自己免受暴力攻击。如果使用基于时间的密码,则在30秒内尝试1000000个值将有100%的机会猜测密码。对于基于HMAC的passowrds(HOTPs),情况似乎更糟
  • 例子 将以下代码用于一次性基于HMAC的密码时:

    secret='MZXW633PN5XW6MZX'
    对于x范围内的i(1,10):
    打印i,获取\u hotp\u令牌(秘密,间隔\u no=i)
    
    您将得到以下结果:

    1 448400
    2 656122
    3 457125
    4 35022
    5 401553
    6 581333
    7 16329
    8 529359
    9 171710
    

    这与Google Authenticator应用程序生成的令牌相对应(除非小于6个符号,否则应用程序会在开始处添加零以达到6个字符的长度)。

    我想要一个python脚本来生成TOTP密码。因此,我编写了python脚本。这是我的实现。我在维基百科上有这篇文章,还有一些关于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)
    

    @burhan:如果您需要这些代码,我已经将其上传到GitHub(此处:),因此在项目中作为单独的模块使用它应该很容易。享受吧!我的代码有问题,因为我试图登录的服务提供的“机密”是小写,而不是大写。将第4行更改为“key=base64.b32decode(secret,True)”为我解决了这个问题。@ChrisMoore:我已经用
    casefold=True
    更新了代码,所以人们现在不应该有类似的问题。谢谢你的输入。我刚刚被一个网站给了一个23个字符的秘密。当我告诉你这个秘密时,你的代码失败了,出现了一个“TypeError:error padding”。像这样填充秘密,修复了问题:key=base64.b32decode(secret+'=='[:3-((len(secret)-1)%4)],True)for python 3:change:
    ord(h[19])&15
    改为:
    o=h[19]&15
    谢谢BTWInteresting,但您可能希望让读者更容易理解。请使变量名更有意义,或添加docstring。此外,遵循PEP8可能会获得更多支持。您是否比较了这两种解决方案的性能