likes
comments
collection
share

OTP的来龙去脉

作者站长头像
站长
· 阅读数 16

一、背景

我们常用的账号密码,就是静态口令,这种登录方式存在很多安全漏洞,例如:密码猜测、字典攻击、穷举攻击、截取/重放、社会工程学攻击等等。这些攻击都可以让恶意攻击者轻易地窃取用户账号的登录凭证,从而对用户的隐私和财务安全造成严重威胁。

为了提高账号的安全性和保护用户的隐私,诞生了许多其他的认证方式,OTP 就是其中的一种。

二、OTP介绍

一次性密码(One Time Password,简称OTP),又称 动态密码单次有效密码,是指计算器系统或其他数字设备上只能使用一次的密码,有效期为只有一次登录会话或交易。OTP 避免了一些与传统基于(静态)密码认证相关系的缺点;一些实现还纳入了双因素认证,确保单次有效密码需要访问一个人有的某件事物(如内置 OTP 计算器的小钥匙挂件设备)以及一个人知道的某件事物(如 PIN)。

三、OTP技术的实现方式

  • 基于计数器的 HOTP(HMAC-based One Time Password):这种方式使用一个计数器和一个密钥来生成动态密码。每次身份验证都会增加计数器的值,以生成一个新的动态密码。由于计数器只会单调递增,因此可以有效地防止同一密码被重复使用。

  • 基于时间的 TOTP(Time-based One Time Password):这种方式使用当前时间戳和一个密钥来生成动态密码。与计数器不同的是,时间戳是单调递增的,并且在固定时间窗口内有效。这样可以避免计数器不同步的问题,同时也提供了更好的便利性,因为用户可以在不同的设备上使用相同的动态密码。

  • 其他算法:还有一些其他的 OTP 实现方式,如基于挑战-响应(Challenge-Response)的算法,或者使用生物特征识别等技术来生成动态密码。

四、OTP实现原理

让我们分开来介绍下,不同实现方式的原理。

4.1 HOTP实现原理

HOTP 利用哈希算法和时间戳来生成具有时效性的一次性密码。HOTP 的实现原理主要包括以下几个方面:

  • 秘钥生成:在使用 HOTP 进行身份验证之前,需要生成一个与客户端预共享的密钥 K。该密钥可以通过安全随机数生成器生成,并保存在服务端和客户端中。

  • 时间戳生成:服务端和客户端都有一个计数器 C,每次进行身份验证时,客户端会向服务端发送计数器的值,服务端会将该值与自己维护的计数器值进行比较。为了防止重放攻击,还可以通过时间戳生成器生成一个 T 值,客户端和服务端各自独立地生成 T 值,并将 T 值与计数器混合后作为 HMAC-SHA1 函数的输入。

  • HMAC-SHA1 计算:客户端和服务端使用相同的密钥 K 和经过混合的 T 值和计数器 C,分别计算 HMAC-SHA1 哈希值 H1 和 H2。HOTP 使用的 HMAC-SHA1 算法具有防篡改性和伪随机性,保证了计算结果的不可预测性和安全性。

  • 动态密码生成:根据计算出的哈希值 H1 或 H2,取其最后 4 位(或其他固定位数),并进行截断、整形和模运算等操作,得到一个 6 位的一次性密码。这个密码是具有时效性的,只有在当前时间段内有效。

  • 验证流程:客户端将生成的一次性密码发送给服务端,服务端基于相同的计算方式和密钥,计算出一次性密码,并与客户端发送的密码进行比较。如果验证通过,则认为该客户端合法。

4.2 TOTP实现原理

与 HOTP 不同的是,TOTP 使用的不是计数器,而是时间戳作为输入进行哈希计算。TOTP 的实现原理主要包括以下几个方面:

  • 秘钥生成:在使用 TOTP 进行身份验证之前,需要生成一个与客户端预共享的密钥 K。这个密钥可以通过安全随机数生成器生成,并保存在服务端和客户端中。

  • 时间戳生成:客户端和服务端都有一个时间戳 T0,在 T0 的基础上每隔一段时间(比如 30 秒)生成一个新的时间戳 T,并将其转换为整型格式。

  • HMAC-SHA1 计算:客户端和服务端使用相同的密钥 K 和当前时间戳 T,分别计算 HMAC-SHA1 哈希值 H1 和 H2。HMAC-SHA1 算法具有防篡改性和伪随机性,保证了计算结果的不可预测性和安全性。

  • 动态密码生成:根据计算出的哈希值 H1 或 H2,取其最后 4 位(或其他固定位数),并进行截断、整形和模运算等操作,得到一个 6 位的一次性密码。这个密码是具有时效性的,只有在当前时间段内有效。

  • 验证流程:客户端将生成的一次性密码发送给服务端,服务端基于相同的计算方式和密钥,计算出一次性密码,并与客户端发送的密码进行比较。如果验证通过,则认为该客户端合法。

4.3 CROTP

CROTP 是一种利用挑战和响应方式来进行身份验证的 OTP 机制。与 HOTP 和 TOTP 不同,它不是在客户端本地生成一次性密码,而是由服务端发起挑战并由客户端进行响应计算。挑战-响应 OTP 的实现原理主要包括以下几个方面:

  • 秘钥生成:在使用挑战-响应 OTP 进行身份验证之前,同样需要生成一个与客户端预共享的密钥 K。这个密钥可以通过安全随机数生成器生成,并保存在服务端和客户端中。
  • 挑战生成:服务端发起一个随机化的挑战 C,通常为一个 64 位(或更多)长度的随机数,将其发送给客户端。
  • 响应计算:客户端收到服务端发起的挑战后,将挑战值和密钥 K 作为 HMAC-SHA1 函数的输入进行一次哈希计算,得到一个固定长度的哈希值 H。然后将该哈希值的最后 6 位(或其他固定位数)返回给服务端作为响应结果。
  • 验证流程:服务端使用相同的密钥 K 和当前时间戳 T,将客户端返回的哈希值进行验证。服务端将收到的哈希值与自己计算出的 H 值进行比较,如果相同,则认为该客户端合法。

五、HMAC

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,是一种利用密码学中的散列函数来进行消息认证的一种机制。

HMAC 中所使用的单向散列函数并不仅限于一种,任何高强度的单向散列函数都可以被用于 HMAC,如果将来设计出的新的单向散列函数,也同样可以使用。使用SHA-1、SHA-224、SHA-256、SHA-384、SHA-512所构造的HMAC,分别称为HMAC-SHA1、HMAC-SHA-224、HMAC-SHA-384、HMAC-SHA-512。

HMAC 将一个密钥 K 和一个消息 M 作为输入,经过一系列的哈希计算得到一个固定长度的 MAC(Message Authentication Code),并将该 MAC 用于验证消息的真实性或防止消息被篡改。

5.1 HOTP中的HMAC

HOTP 算法中,首先需要生成一个对称密钥 K(由服务端和客户端共享),以及一个初始计数器值 C(也是由服务端和客户端共享)。然后,利用 HMAC-SHA1 算法计算一次性密码。

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

上面采用了 HMAC-SHA-1,当然也可以使用其他的散列算法。HMAC 算法得出的值位数比较多,不方便用户输入,因此需要截断(Truncate)成为一组不太长十进制数(例如6位)。计算完成之后客户端计数器 C 计数值加1。

5.2 TOTP中的HMAC

与 HOTP 不同的是,TOTP 中的计数器不再是一个静态值,而是基于时间产生的动态值。也就是公式中的 C 变化了。

六、OTP代码实现

引入依赖包(Github 上一位大佬的,使用特别简单)。

<dependency>
    <groupId>com.github.bastiaanjansen</groupId>
    <artifactId>otp-java</artifactId>
    <version>2.0.1</version>
</dependency>

6.1 HOTP的JAVA实现

/**
 * HOTP实现
 */
static void hotp() {
    // 生成密钥
    byte[] secret = SecretGenerator.generate();
    HOTPGenerator hotp = new HOTPGenerator.Builder(secret)
            // 一次性密码的长度
            .withPasswordLength(6)
            // 散列算法
            .withAlgorithm(HMACAlgorithm.SHA512)
            .build();
    //  计数器值
    int counter = 2;
    // 生成一次性密码
    String code = hotp.generate(counter);
    System.out.println("hotp生成的一次性密码:" + code);
    // 验证一次性密码
    boolean verify = hotp.verify(code, counter);
    System.out.println("hotp验证一次性密码结果:" + verify);
}

6.2 TOTP的JAVA实现

/**
 * TOTP实现
 */
static void totp() {
    // 生成密钥
    byte[] secret = SecretGenerator.generate();
    TOTPGenerator totp = new TOTPGenerator.Builder(secret)
            .withHOTPGenerator(builder -> {
                // 一次性密码的长度
                builder.withPasswordLength(6);
                // 散列算法
                builder.withAlgorithm(HMACAlgorithm.SHA512);
            })
            // 时间周期
            .withPeriod(Duration.ofSeconds(30))
            .build();
    // 生成一次性密码
    String code = totp.now();
    System.out.println("totp生成的一次性密码:" + code);
    boolean verify = totp.verify(code);
    System.out.println("totp验证一次性密码结果:" + verify);
}

6.3 使用

让我们来调用一次看看。

public static void main(String[] args) {
    hotp();
    totp();
}

打印如下:

hotp生成的一次性密码:971935
hotp验证一次性密码结果:true
totp生成的一次性密码:001566
totp验证一次性密码结果:true

6.4 调试

生成otpauth。

System.out.println(totp.getURI("issuer","account"));

大概这个样子:

otpauth://totp/issuer:account?period=30&digits=6&secret=6AVUZT5NK2AUJNQT2TZK27NIEW334AYS&issuer=issuer&algorithm=SHA512

找一个二维码生成网站。

OTP的来龙去脉

可以手机下载 Google Authenticator,扫码即可。

OTP的来龙去脉

生成的一次性密码,可以用上边的验证代码来调试。

转载自:https://juejin.cn/post/7243787613646454844
评论
请登录