一次性密码方案

  1. 背景

    OTP一次性密码常见于后台登陆,敏感操作的二次验证中,常用方案有HOTP(基于HMAC的一次性密码)、TOTP(基于时间的一次性密码)、短信验证、PUSH验证等,本文只讨论实现成本低的HOTP、TOTP

  2. HOTP: HMAC-based One-time Password algorithm

    基于散列消息验证码的一次性密码算法

    HOTP使用对称验证方式:即验证双方使用相同的Hash算法 H、密钥K、运算值长度,以及“相同”的随机值C,验证双方分别计算得出相同的则验证成功,关于“相同”的随机值C含义后文会介绍。HOTP对于Hash1算法没有特别要求但建议采用SHA-256,密码长度6-10位,建议使用6位或8位,目前市面上常见的二次验证大多采用6位。

    • 名词解释
      • HMAC:哈希算法
      • K:密钥
      • C:随机值(此值验证双方要一致)
    • 实现方式 SHA-1为例子
      1. HMAC(K, C)

        Hash结果为20字节字符串MAC

      2. truncate(MAC) = extract31(MAC, MAC[(19×8) + 4:(19×8)+7] × 8)

        最后1个字节的高地址4位表示成整数i,作为下一步截取偏移量。

      3. extract31(MAC, i) = MAC[i+1 : i+(4 × 8)−1]

        从原始Hash值中从 i+1到i+(4 × 8)−1 位截取4个bit,结果转成整数,不足6位前面补0。

    • 缺点

    如果上面计算过程我解释清楚了,那么你应该会有个疑问,K为密钥那么验证双方有记录就可以,C怎么保持一致,正如前文提到C需要相同,一般实现上C采用计数器方式,每次验证后验证双方同时加1,为了容忍一方计数器落后可采用验证方在C±n,即在一个可控范围内验证通过,即便如此工程上实现依旧有难度或者实现不优雅,所以才会有TOTP:基于时间的一次性密码出现。

  3. TOTP: Time-based One-time Password algorithm

    TOTP一次性密码需要验证端与被验证端时间同步,验证方式与HOTP类似,上面计算过程中的C改为时间戳,下面我们以Google验证器的实现解析TOTP的原理。

    • Google Authenticator 计算过程
    function GoogleAuthenticatorCode(string secret)
    message := floor(current Unix time / 30)
    hash := HMAC-SHA1(secret, message)
    offset := last nibble of hash
    truncatedHash := hash[offset..offset+3] //4 bytes starting at the offset
    Set the first bit of truncatedHash to zero //remove the most significant bit
    code := truncatedHash mod 1000000
    pad code with 0 from the left until length of code is 6
    return code

    可以看出计算方式与HOTP一致

    我们把floor(current Unix time / 30)记做T,验证端分别使用C = T-1、T、T+1 计算三次,就可以在两端30秒的误差内实现验证一致,从而避免了两端时间不一致造成的验证失败,也使一次性密码的有效期保证最低1分钟有效,实现上只要保证验证双方采用相同的密钥,时间差不超过30秒就能做到一次性密码校验。

  4. 开源实现推荐