首页 多因素认证:TOTP算法
文章
取消

多因素认证:TOTP算法

介绍

  多因素认证Multi-Factor Authentication(MFA)是一种非常简单的安全实践方法,它能够在用户名称和密码之外再额外增加一层保护。启用多因素认证后,用户进行操作时,除了需要提供用户名和密码外(第一次身份验证),还需要提供验证码(第二次身份验证),多因素身份认证结合起来将为您的账号和资源提供更高的安全保护。目前比较流行的多因素认证主要是接收短信码和TOTP算法:

  • 接收短信码:通过预留手机号接收短信码,回填到系统,通过比对短信码来做二次身份验证。一般应用于前台用户登录认证。
  • TOTP算法:手机下载一个应用,通过登录后扫码绑定一个密钥,应用会不断生成动态验证码,通过输入动态验证码做二次认证。一般用于后台账号的登录认证。

TOTP

  TOTP算法(Time-based One-time Password algorithm)是一种从共享密钥和当前时间计算一次性密码的算法。 它已被采纳为Internet工程任务组标准RFC 6238,是Initiative for Open Authentication(OATH)的基石,并被用于许多双因素身份验证系统。

  TOTP是基于散列的消息认证码(HMAC)的示例。 它使用加密哈希函数将密钥与当前时间戳组合在一起以生成一次性密码。 由于网络延迟和不同步时钟可能导致密码接收者必须尝试一系列可能的时间来进行身份验证,因此时间戳通常以30秒的间隔增加,从而减少了潜在的搜索空间。

步骤

1、 密钥生成:在为帐户设置TOTP时,将生成一个唯一的密钥。该密钥在用户设备和认证服务器之间共享。

2、 时间同步:用户设备和认证服务器需要同步到相同的时间源。TOTP算法依赖于当前时间来生成一次性密码。

3、 OTP生成:TOTP算法将密钥和当前时间结合起来,应用Hash函数生成一次性密码。此密码通常包含6-8位数字,并具有有限的有效期,例如30或60秒。

4、 OTP验证:认证时,由TOTP应用程序或设备生成的一次性密码,认证服务器根据共享密钥和当前时间独立生成预期的TOTP。如果用户输入的TOTP与服务器生成的TOTP匹配,则认证成。

算法

公式:

TC = (T - T0) / TS
TOTP(K, TC) = Truncate(HMAC-SHA-1(K, TC))
  • T:当前的时间戳
  • T0:起始时间,一般为0
  • TS:时间间隔,步长
  • K:密钥
  • HMAC-SHA-1:表示使用SHA-1做HMAC
  • Truncate,是一个函数,用于截取加密后的字符串

代码实现(Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import org.apache.commons.codec.binary.Base32;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Date;

public class Totp {

    /**
     * 密钥
     */
    private static final String K = "";

    /**
     * 初始化时间
     */
    private static final long T0 = 0;

    /**
     * 时间步长 单位:毫秒 作为口令变化的时间周期
     */
    private static final long TS = 30000;

    /**
     * 转码位数 [1-8]
     */
    private static final int CODE_DIGITS = 6;

    /**
     * 生成密钥,每用户一份密钥
     * @return String
     */
    public static String getSecretKey() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[10];
        secureRandom.nextBytes(bytes);
        Base32 base32 = new Base32();
        String secretKey = base32.encodeToString(bytes);
        return secretKey.toUpperCase();
    }

    /**
     * 获取当前动态因子
     *
     * @return long
     */
    private static long timeFactor() {
        long T = new Date().getTime();
        return (T-T0) / TS;
    }

    /**
     * 哈希加密
     *
     * @param key 密钥串
     * @param tc     加密内容
     * @return byte[]
     */
    private static byte[] hmacSha1(String key, long tc) throws NoSuchAlgorithmException, InvalidKeyException {
            Mac mac = Mac.getInstance("HmacSHA1");
            Base32 codec = new Base32();
            byte[] keyBytesBase32 = codec.decode(key.getBytes());
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytesBase32, "RAW");
            mac.init(secretKeySpec);
            byte[] input = ByteBuffer.allocate(8).putLong(tc).array();
            return mac.doFinal(input);
    }

    private static String truncate(byte[] hash) {
        // System.out.printf("hash2=%s\n", Arrays.toString(hash));
        int offset = hash[hash.length - 1] & 0xf;

        long binary = ((hash[offset] & 0x7f) << 24)
                | ((hash[offset + 1] & 0xff) << 16)
                | ((hash[offset + 2] & 0xff) << 8)
                | (hash[offset + 3] & 0xff);

        int code = (int)(binary % Math.pow(10,  CODE_DIGITS));
        return String.format("%0" + CODE_DIGITS + "d", code);
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        String k = getSecretKey();
        System.out.println("K:"+k);

        long time = timeFactor();
        System.out.println("timeFactor:"+time);

        byte[] h = hmacSha1(k,time );
        System.out.println("hash:"+Arrays.toString(h));

        String t = truncate(h);
        System.out.println("code:"+t);
    }
}

结束

  通过TOTP算法实现的多因素认证一般是手机端安装一个应用,这也极大的限制了应用场景,毕竟让前端客户手机上安装一个仅仅用于安全验证的应用还是很困难的,而且一旦换手机都需要一定的操作才能继续使用,否则将无法登录。所有该方法一般用于后台的认证较多。其实不需要自己开发移动端应用,目前Google和微软都有免费应用可以使用。

苹果应用

本文由作者按照 CC BY 4.0 进行授权

云平台-microk8s(四):nginx部署

java基础-注解处理器:Processor