评论

收藏

【IoT】加密与安全:动态令牌 OTP、HOTP、TOTP 原理解析

网络安全 网络安全 发布于:2021-07-13 10:18 | 阅读数:488 | 评论:0

  1、OTP、HOTP、TOTP 简介
  1.1、OTP
  One-Time Password 简写,表示一次性密码。
  1.2、HOTP
  HMAC-based One-Time Password 简写,表示基于 HMAC 算法加密的一次性密码。是事件同步,通过某一特定的事件次序及相同的种子值作为输入,通过 HASH 算法运算出一致的密码。
  1.3、TOTP
  Time-based One-Time Password 简写,表示基于时间戳算法的一次性密码。 
  时间同步,基于客户端的动态口令和动态口令验证服务器的时间比对,一般每 60 秒产生一个新口令,要求客户端和服务器能够十分精确的保持正确的时钟,客户端和服务端基于时间计算的动态口令才能一致。  
  2、TOTP 弱点和漏洞编辑
  TOTP 代码可以像密码一样被钓鱼,但它们需要网络钓鱼者实时代理凭证,而不是及时收集它们,不限制登录尝试的实现容易受到强制执行代码的***。
  窃取共享密钥的***者可以随意生成新的有效 TOTP 代码。如果***者破坏了大型身份验证数据库,这可能是一个特殊问题。
  由于 TOTP 设备的电池电量不足,时钟可以解除同步,并且由于软件版本在用户可能丢失或被盗的手机上,因此所有实际实施都有绕过保护的方法(例如:打印的代码,电子邮件 - 重置等),这可能给大型用户群带来相当大的支持负担,并且还为欺诈用户提供额外的利用向量。
  TOTP 代码的有效期超过它们在屏幕上显示的时间(通常是两倍或更多倍)。这是一个让步,认证和认证方的时钟可以大幅度扭曲。
所有一次性基于密码的身份验证方案(包括 TOTP 和 HOTP 等)仍然容易受到会话劫持,即在用户登录后占用用户的会话。
  3、原理介绍
  3.1、OTP 基本原理
  计算 OTP 串的公式:
  OTP(K,C) = Truncate(HMAC-SHA-1(K,C))
  其中,
  K 表示秘钥串;
  C 是一个数字,表示随机数;
  HMAC-SHA-1 表示使用 SHA-1 做 HMAC;
  Truncate 是一个函数,就是怎么截取加密后的串,并取加密后串的哪些字段组成一个数字。
  对 HMAC-SHA-1 方式加密来说,Truncate 实现如下:
  HMAC-SHA-1 加密后的长度得到一个 20 字节的密串;
  取这个 20 字节的密串的最后一个字节,取这字节的低 4 位,作为截取加密串的下标偏移量;
  按照下标偏移量开始,获取4个字节,按照大端方式组成一个整数;
  截取这个整数的后 6 位或者 8 位转成字符串返回。
  示例:
  Java 代码实现:
public static String generateOTP(String K,
                                     String C,
                                     String returnDigits,
                                     String crypto){
       int codeDigits = Integer.decode(returnDigits).intValue();
       String result = null;
 
       // K是密码
       // C是产生的随机数
       // crypto是加密算法 HMAC-SHA-1
       byte[] hash = hmac_sha(crypto, K, C);
       // hash为20字节的字符串
 
       // put selected bytes into result int
       // 获取hash最后一个字节的低4位,作为选择结果的开始下标偏移
       int offset = hash[hash.length - 1] & 0xf;
 
       // 获取4个字节组成一个整数,其中第一个字节最高位为符号位,不获取,使用0x7f
       int binary =
               ((hash[offset] & 0x7f) << 24) |
               ((hash[offset + 1] & 0xff) << 16) |
               ((hash[offset + 2] & 0xff) << 8) |
               (hash[offset + 3] & 0xff);
       // 获取这个整数的后6位(可以根据需要取后8位)
       int otp = binary % 1000000;
       // 将数字转成字符串,不够6位前面补0
       result = Integer.toString(otp);
       while (result.length() < codeDigits) {
           result = "0" + result;
       }
       return result;
   }
  返回的结果就是看到一个数字的动态密码。
  3.2、HOTP 基本原理
  知道了 OTP 的基本原理,HOTP 只是将其中的参数 C 变成了随机数。
  HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
  HOTP: 
  Generates the OTP for the given count
  C 作为一个参数,获取动态密码。
  示例:
  HOTP 的 python 代码片段:
class HOTP(OTP):
    def at(self, count):
        """
        Generates the OTP for the given count
        @param [Integer] count counter
        @returns [Integer] OTP
        """
        return self.generate_otp(count)
  一般规定 HOTP 的散列函数使用 SHA2,即:基于 SHA-256 or SHA-512 [SHA2] 的散列函数做事件同步验证;
  3.3、TOTP 基本原理
  TOTP 只是将其中的参数 C 变成了由时间戳产生的数字。
TOTP(K,C) = HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
  不同点是 TOTP 中的 C 是时间戳计算得出。
C = (T - T0) / X;
  T 表示当前 Unix 时间戳:
DSC0000.png

  T0 一般取值为 0,也就是 1970 年 1 月 1 日。
  因此上式可以简化为:
C = T / X;
  X 表示时间步数,也就是说多长时间产生一个动态密码,这个时间间隔就是时间步数 X,系统默认是 30 秒;
  例如:
T0 = 0;
X = 30;
  简化为:C = T / 30
  1)T = 30 ~ 59
C = 1;
表示 30 ~ 59 这  30  秒内的动态密码一致。
  2)T = 60 ~ 89
C = 2; 
表示 30 ~ 59 这 30 秒内的动态密码一致。
  不同厂家使用的时间步数不同:
阿里巴巴的身份宝使用的时间步数是 60 秒;
宁盾令牌使用的时间步数是 60 秒;
Google 的身份验证器的时间步数是 30 秒;
腾讯的 Token 时间步数是 60 秒;
  TOTP 的 python 代码片段:
class TOTP(OTP):
    def __init__(self, *args, **kwargs):
        """
        @option options [Integer] interval (30) the time interval in seconds
            for OTP This defaults to 30 which is standard.
        """
        self.interval = kwargs.pop('interval', 30)
        super(TOTP, self).__init__(*args, **kwargs)
    def now(self):
        """
        Generate the current time OTP
        @return [Integer] the OTP as an integer
        """
        return self.generate_otp(self.timecode(datetime.datetime.now()))
 
    def timecode(self, for_time):
        i = time.mktime(for_time.timetuple())
        return int(i / self.interval)
  self.interval 是时间步数 X;
  datetime.datetime.now() 为当前的 Unix 时间戳;
  timecode 表示 (T - T0) / X,即获取获取动态密码计算的随机数。
  TOTP 的实现可以使用 HMAC-SHA-256 或者 HMAC-SHA-512 散列函数;
  TOTP 的要求:
  客户端和服务器必须能够彼此知道或者推算出对方的 Unix Time;
  客户端和服务器端必须共享一个密钥;
  算法必须使用 HOTP 作为其关键实现环节;
  客户端和服务器端必须使用相同的步长 X;
  每一个客户端必须拥有不同的密钥;
  密钥的生成必须足够随机;
  密钥必须储存在防篡改的设备上,而且不能在不安全的情况下被访问或使用;
  对该算法中 T 的实现必须大于 int32,因为它在 2038 年将超出上限;
  T0 和 X 的协商必须在之前的步骤中就已经做好了。
  4、参考
  4.1、python 的 otp 实现
https://pypi.python.org/pypi/pyotp
https://github.com/pyotp/pyotp
  4.2、基于 pyotp 的简单应用
>>> import base64
>>> base64.b32encode('This is my secret key')
'KRUGS4ZANFZSA3LZEBZWKY3SMV2CA23FPE======'
>>> secretKey = base64.b32encode('This is my secret key')
>>> import pyotp
>>> totp = pyotp.TOTP(secretKey)
>>> totp.now()
423779
  简单说明:
  加载base64的模块,将我的秘钥做一下base32的加密,加载pyotp模块,otp使用base32加密后的秘钥传作为种子,生成随机数字验证的。
  可以使用pyotp和expect一起实现基于google authenticator的自动登录(免去每次双认证,输入密码和动态密码)。
  4.3、pyotp 的 TOTP 的使用说明
totp = pyotp.TOTP('base32secret3232')
totp.now() # => 492039
 
# OTP verified for current time
totp.verify(492039) # => True
time.sleep(30)
totp.verify(492039) # => False
  4.4、pyotp 的 HOTP 的使用说明
hotp = pyotp.HOTP('base32secret3232')
hotp.at(0) # => 260182
hotp.at(1) # => 55283
hotp.at(1401) # => 316439
 
# OTP verified with a counter
hotp.verify(316439, 1401) # => True
hotp.verify(316439, 1402) # => False
  4.5、使用场景
服务器登录动态密码验证(如阿里云ECS登录,腾讯机房服务器登录等);
公司***登录双因素验证;
网络接入radius动态密码;
银行转账动态密码;
网银、网络游戏的实体动态口令牌;
等动态密码验证的应用场景。
  4.6、市面上基于 HOTP 的产品
宁盾令牌
阿里巴巴的身份宝 
Google 的身份验证器(google-authenticator)
  4.7、Google 基于 TOTP 的开源实现
https://github.com/google/google-authenticator
RFC6238中TOTP基于java代码的实现。
  4.8、golang 的一个 otp 实现
https://github.com/gitchs/gootp
  4.9、RFC 参考
RFC 4226 One-Time Password and HMAC-based One-Time Password.
RFC 6238 Time-based One-Time Password.
RFC 2104 HMAC Keyed-Hashing for Message Authentication.
  
关注下面的标签,发现更多相似文章