主页

引言

在2FA双因素认证中,TOTP可谓是标准化程度最高的技术方案。它已被互联网工程任务组接纳为RFC 6238标准,成为OATH(开放标准,用于授权和身份认证)的基石,并被用于众多多重要素验证系统当中。本文将进一步介绍TOTP的工作原理以及相关密码技术应用,并用代码示例来剖析底层细节。以便于读者能够根据需要实现自己的TOTP双因素认证机制。

本文内容组织:

  • 简介
  • TOTP工作原理
    • 共享密钥生成
    • OTP生成算法
  • 代码实战
  • 总结
  • 参考资料

简介

TOTP定义:基于时间的一次性密码算法(英语:Time-based One-Time Password,简称:TOTP)是一种根据共享密钥当前时间计算一次性密码的算法。(该定义来自wikipedia)

从定义上我们可以了解到:

  • TOTP是一种算法,该算法主要用来生成一次密码(这里的一次密码可以理解为仅能使用一次的验证码
  • TOTP基于”预共享密钥“,TOTP在使用过程中一般涉及到两端(客户端和服务端),客户端和服务端分别生成验证码,生成过程需要基于预共享密钥。该共享密钥主要保证安全性,在不知道该密钥的情况下,第三方或攻击者无法生成相同的验证码
  • TOTP基于”当前时间“,当前时间会作为TOTP算法的随机因子,保证验证码的一次性

TOTP工作原理

《2FA双因素认证 - 原理和应用》 一文中,我们简单提到过TOTP的工作原理:

从上图中,我们可以看到客户端(用户手机)和服务端(servers)分别计算出了 相同验证码,因此用户在使用该验证码时,会被验证通过。

要深入理解TOTP的工作原理,我们需要注意图中的以下几点,后文我们将重点介绍:

  • OTP密钥:该密钥就是“共享密钥”
  • HMAC:HMAC是一种密码技术,可以将任意长度的输入转换为固定长度的输出, 在转换过程中依赖于密钥保证安全性(这里的密钥就是“共享密钥”),输出结果的长度依赖于HMAC依赖的哈希算法。
  • 截取:由于HMAC输出的结果一般较长,而为了方便记忆和在终端输入,我们平时使用的验证码都是4-6位整数。所以这里需要对HMAC结果进行截取处理。

TOTP详细流程如下:

TOTP认证主要包含三个阶段:

  • 密钥共享
  • 一次密码OTP获取
  • 一次密码OTP验证

密钥共享

共享密钥由服务端生成,并通过安全的通信渠道发送给用户,或者通过安全的密钥交换协议传输给用户。这个密钥通常是一个Base32编码的字符串,可以包含字母(A-Z)和数字(2-7),长度一般为16字节。生成密钥时要确保使用足够的随机性,可以使用安全的随机数生成器。

在TOTP rfc6238文档中并未给出详细的共享密钥生成算法,而是直接使用了HTOP rfc4226文档中<7.5 Management of Shared Secrets>介绍的方式。

在HTOP rfc4226中给出了两种方式来生成共享密钥:确定生成方式 和 随机生成方式

确定生成方式

共享密钥确定生成方式如下:

  • 服务端维护一个主密钥(MK = Master Key)
  • 共享密钥使用算法K_i = SHA-1 (MK,i)基于主密钥生成,这里i 代表用户序号,一般一个用户对应一个i, 用于区分不同用户端

注:这里的确定生成方式指的是生成共享密钥是确定的,但生成主密钥MK是随机数的,并且需要安全保存。在服务端验证用户登录时输入的一次密码时,需要基于用户序号和主密钥生成共享密钥。

该方式的优点是,服务端只需要保护一个主密钥,密钥管理简单;但如果主密钥丢失,则所有共享密钥都存在安全风险。

随机生成方式

  • 服务端不依赖于主密钥
  • 服务端会为每个客户端(用户)生成唯一的,与其他用户无关的随机数,作为共享密钥。

该方式的优点是,不同共享密钥之间时独立无关的,服务端一个共享密钥的丢失不会对其他共享密钥产生安全隐患;但是如果客户端或用户较多,密钥管理负担较重。

OTP生成算法

《2FA双因素认证 - 原理和应用》 一文中,我们已经提到过OTP生成算法,如下:

  #  T表示当前时间,T0表示初始时间(一般为0,可省略)
# Period表示更新周期(一般为30秒)
# C表示基于时间生成的计数
C = (T - T0) / Period # K:加密密码,作为HMAC密码算法输入,由于只有客户端和服务端共享,因此在不知道K的情况下,无法生成,保证安全性。
# C:计数器,客户端和服务端基于本地时间分别计算
# h:表示使用密码技术得到的一次密码(但是由于h长度较大,不适合作为验证码,因此需要进一步截取
h = HMAC(K, C) # otp:one time password(一次密码),通过对h进行截取处理(这里的digit代表需要截取的位数,通常情况下为6)
otp = Trunc(h, digit)

在实际应用中,TOTP生成算法T0一般为0,Period一般为30秒,一次密码长度为6,则TOTP生成算法如下:

TOTP(K,C) = Truncate(HMAC-SHA-1(K,T/30)

注:HMAC-SHA-1表示HMAC底层使用了SHA-1哈希算法,该算法输出为160位二进制 = 20字节, 因此需要使用Truncate算法进行截取,该算法如下:

        int offset   =  hmac_result[19] & 0xf ;
int bin_code = (hmac_result[offset] & 0x7f) << 24
| (hmac_result[offset+1] & 0xff) << 16
| (hmac_result[offset+2] & 0xff) << 8
| (hmac_result[offset+3] & 0xff) ;

为了方便理解,对于Truncate算法我们可以通过例子解释一下,假设HMAC-SHA-1生成的结果如下:

   -------------------------------------------------------------
| Byte Number |
-------------------------------------------------------------
|00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|
-------------------------------------------------------------
| Byte Value |
-------------------------------------------------------------
|1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a|
-------------------------------***********----------------++| - 最后一个字节(第19个字节)的十六进制值为0x5a。
- 低4位的值为0xa(偏移值)。
- 偏移值为字节10(0xa)。
- 从第10个字节开始的4个字节的值为0x50ef7f19,即动态二进制码DBC1。
- DBC1的最高有效位为0x50,因此DBC1 = 0x50ef7f19。
然后,我们将这个数对1,000,000(10^6)取模,以生成6位的TOTP值872921(十进制)。

Demo演示

我们使用golang语言demo来演示Google Authenticator中OTP生成和验证。

golang代码

下面为Google Authenticator详细实现,以下代码可直接copy并运行。

/**
* @Author: warm3snow
* @File: google_authenticator.go
* @Version: 1.0.0
* @Description: desc.
* @Date: 2023/8/12 18:32
*/ package main import (
"crypto/hmac"
"crypto/sha1"
"encoding/base32"
"encoding/binary"
"fmt"
"time"
) func generateTOTP(secret string, counter int64) (string, error) {
// 1. 共享密钥解码:将Base32编码的密钥解码为字节数组
decodedKey, err := base32.StdEncoding.DecodeString(secret)
if err != nil {
return "", err
} // 2. HMAC-SHA-1:计算哈希值
h := hmac.New(sha1.New, decodedKey)
binary.Write(h, binary.BigEndian, counter) // 3. Truncate:对哈希值进行截断,只保留最后 4 个字节
offset := h.Sum(nil)[19] & 0x0f
truncatedHash := h.Sum(nil)[offset : offset+4]
truncatedHash[0] = truncatedHash[0] & 0x7f
otp := (int32(truncatedHash[0])<<24 | int32(truncatedHash[1])<<16 | int32(truncatedHash[2])<<8 | int32(truncatedHash[3])) % 1000000 // 4. 格式化:将结果转换为十进制6位的字符串
return fmt.Sprintf("%06d", otp), nil
} func main() {
// 设置共享密钥(这里使用Base32编码)
secret := "ABCDEFGH234567MN" // 获取当前时间步长C
currentTime := time.Now().Unix()
timeStep := currentTime / 30 // 生成TOTP
otp, err := generateTOTP(secret, timeStep)
if err != nil {
panic(err)
} fmt.Println("Generated OTP:", otp)
}

注:以上代码已经开源到github,地址:https://github.com/warm3snow/practical-crypto/blob/master/tools/authenticator/google_authenticator/google_authenticator.go

测试

在测试之前,首先进行初始化说明:

  • 测试中使用的共享密钥为ABCDEFGH234567MN,该密钥使用base32编码(具体编解码算法可以google,这里不再赘述)
  • 测试中使用的更新周期或时间步长为30

使用demo程序生成OTP

打开终端并执行:

➜ git clone https://github.com/warm3snow/practical-crypto.git
➜ cd tools/authenticator/google_authenticator ➜ google_authenticator git:(master) ✗ go run google_authenticator.go
共享密钥:ABCDEFGH234567MN
Current unix time: 1692457094
Current time counter: 56415236
Generated OTP: 400251 #这是运行demo程序时,生成的otp

使用Google Authenticator官方app生成OTP

  1. 在手机应用商店搜索google authenticator并下载安装(详情略)

  2. 首次打开google authenticator,选择添加动态密码

  3. 设置首个账号。这里有两种方式设置账号,扫描二维码或手动输入设置密钥,我们这里选择”输入设置密钥“

  4. 输入我们在demo中使用的密钥(账号名我们这里可以随意填写,对生成totp不影响)

  5. 添加成功后,我们会发现app已经显示了一个6位的一次密码otp(该密码与我们使用demo程序生成的一致)

总结

本文我们对TOTP共享密钥生成原理、一次密钥生成和验证进行了详细介绍,并通过demo演示,同时与Google官方的Authenticator进行了一致性比对。TOTP作为一种标准化程度较高的2FA认证方式,已经越来越普遍,在网络身份认证中应用2FA技术,对于系统安全性、用户身份安全具有重要意义。

参考资料

2FA双因素认证 - TOTP详解的更多相关文章

  1. 业余草双因素认证(2FA)教程

    所谓认证(authentication)就是确认用户的身份,是网站登录必不可少的步骤.密码是最常见的认证方法,但是不安全,容易泄露和冒充.越来越多的地方,要求启用双因素认证(Two-factor au ...

  2. 双因素认证(2FA)教程

    所谓认证(authentication)就是确认用户的身份,是网站登录必不可少的步骤. 密码是最常见的认证方法,但是不安全,容易泄露和冒充. 越来越多的地方,要求启用 双因素认证(Two-factor ...

  3. 用Abp实现双因素认证(Two-Factor Authentication, 2FA)登录(一):认证模块

    @ 目录 原理 用户验证码校验模块 双因素认证模块 改写登录 在之前的博文 用Abp实现短信验证码免密登录(一):短信校验模块 一文中,我们实现了用户验证码校验模块,今天来拓展这个模块,使Abp用户系 ...

  4. Linux 利用Google Authenticator实现ssh登录双因素认证

    1.介绍 双因素认证:双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统.双因素认证是一种采用时间同步技术的系统,采用了基于时间.事件和密钥三变量而产生的一 ...

  5. Linux 之 利用Google Authenticator实现用户双因素认证

    一.介绍:什么是双因素认证 双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统.双因素认证是一种采用时间同步技术的系统,采用了基于时间.事件和密钥三变量而产 ...

  6. Microsoft Office Specialist (MOS) 认证考试详解---word 2010 部分

    Microsoft Office Specialist ( MOS)认证考试详解 首先是   Microsoft Certification overview http://www.microsoft ...

  7. 轻松搭建CAS 5.x系列(8)-在CAS Server增加双因素认证(DUO版)

    概述说明 为了让系统更加安全,很多登录会加入双因素认证.何为双因素,如果把登陆作为开一扇门的话,那就是在原来的锁上再加一把锁,第二锁用新的钥匙,这样安全系数就更加高了. CAS是通过账号名和密码来认证 ...

  8. Linux双网卡绑定bond详解--单网卡绑定多个IP

    Linux双网卡绑定bond详解 1 什么是bond 网卡bond是通过多张网卡绑定为一个逻辑网卡,实现本地网卡的冗余,带宽扩容和负载均衡,在生产场景中是一种常用的技术.Kernels 2.4.12及 ...

  9. WS Security 认证方式详解

    本文参考文档如下: MSDN 官方详解 : http://www.microsoft.com/china/MSDN/library/WebServices/WebServices/HowASP.NET ...

  10. java 双因素认证(2FA)TOTP demo

    TOTP 的全称是"基于时间的一次性密码"(Time-based One-time Password).它是公认的可靠解决方案,已经写入国际标准 RFC6238. 很早就知道有这个 ...

随机推荐

  1. ShardingSphere

    目录 1.ShardingSphere分表与分库分表 2.ShardingSphere分库分表查询 3.自定义分片算法实现range查询 4.SPI扩展机制概述 5.stand通过SPI实现range ...

  2. endnote文献使用简明教程+遇到问题

    安装下载endnote 1.双击[EndNote X9 v19.0.0.12062 Setup.msi]安装EndNote X9,安装时选择试用,安装完成后不要运行EndNote: 2.如果想使用汉化 ...

  3. 5.9 Windows驱动开发:内核InlineHook挂钩技术

    在上一章<内核LDE64引擎计算汇编长度>中,LyShark教大家如何通过LDE64引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的InlineHook函数挂钩其实 ...

  4. Mac 下安装 mysqlclient

    brew install mysql export LDFLAGS="-L/usr/local/opt/openssl/lib" export CPPFLAGS="-I/ ...

  5. html的input type=file

    文件上传:https://www.zhangxinxu.com/wordpress/2015/11/html-input-type-file/ some与every的使用:https://blog.c ...

  6. 天玑9300大战骁龙8 Gen3:十余项数据实测 到底谁才是安卓之王?

    一.前言:全大核天玑9300正面硬钢骁龙8 Gen3 究竟谁才是安卓芯片之王? 今年,两家移动芯片厂商都开始放大招了,骁龙首发Cortex-X4超大核,联发科也不甘示弱,初次将"全大核&qu ...

  7. 物联网浏览器(IoTBrowser)-MQTT协议集成和测试

    一.简介 MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议.它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情 ...

  8. 下载Apache软件基金的软件和项目(Hadoop相关组件)

    一.下载Hadoop相关组件,可以到Apache软件基金的资源目录: Apache 分发目录地址:https://dlcdn.apache.org/ 二.下载软件 方法一:在页面中找到需要下载的软件目 ...

  9. JS leetcode 两个数组的交集I II 合集题解分析

    壹 ❀ 引 前些日子,在与博客园用户MrSmileZhu闲聊中,我问到了他先前在字节跳动面试中遇到了哪些算法题(又戳到了他的伤心处),因为当时面试的高度紧张,原题描述已经无法重现了,但大概与数组合并. ...

  10. 从零开始的微信小程序入门教程(二),初识WXML与WXSS

    壹 ❀ 引 时隔大半年,我终于开始写小程序入门教程的第二篇了,其实我也在纳闷,这么久的时间我到底干了什么,仔细一想,我学了JavaScript部分进阶知识,学了ES6,系统性的去复习了angularj ...