【TOTP】基于时间的动态密码及其工程实践

这篇具有很好参考价值的文章主要介绍了【TOTP】基于时间的动态密码及其工程实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

探究了常见的动态密码的实现方式及其底层原理,并基于java做出了工程实践。


A.来源于一个现象的好奇

用过公司vpn的都知道,不管是阿里郎还是字节那个连vpn的工具(叫啥记不得了),在电脑连上的时候,都需要手机端做一个二次校验,输入六位数字,这个六位数字每隔几十秒会变动一下。
对于这个东东,有时候好奇心驱使,会去想,这是怎么实现的呢?
最开始我想的一个方案:

  • 客户端维护一个定时器,每隔几十秒去请求一个获取验证码的接口,每次请求的时候,里面存储的验证码会被刷新。

这种想法很合理(起初),逐渐往深处想,这个二次验证(2FA)是干嘛用的呢?

  • 当我们的办公电脑不在内网环境的时候,需要通过这个vpn软件连接内网,当连接上内网后一段时间不使用,会自动注销。
  • 这个连接vpn的软件默认是登录了我们的公司账号的。
  • 如果其他人在我们电脑打开的状态下接触到了我们的电脑,那么会因为无法输入验证码而无法进入公司内网。

这个二次验证的功能就是这样没错了,直到有一天,意外出现了:

  • 手机打开飞行模式忘关了,然后我打开阿里郎,像往常一样在电脑上输入阿里郎的动态验证码,连接内网。
  • 奇怪的事情发生了,竟然连上了!!!
  • 这样我们上面的假想就推翻了,因为上面假想的是要去请求特定的接口来刷新验证码,按照这个逻辑,验证码肯定是服务端生成的。但是现在没网的状态下,也能正常登陆上,说明这个验证码的生成逻辑在客户端!!!

是怎样实现,服务端和客户端不进行通信的情况下,也能做二次验证的,这点引起了我的好奇心,于是开始了查找资料的过程, 就有了后面的梳理和实现。

说到这里,突然想起还有一个现象,就是在大概七八年前,银行卡进行支付的时候,有时候需要输入一个动态密码卡里面的动态密码才能正常支付,这个卡很明显是没联网的,但是也能实现安全的验证,现在想来和上面这个现象应该是一样的道理。

B.2FA

首先我们要了解一下什么是2FA,2FA的全称是双重身份验证,是一种为帐户增加额外安全性的方法。往往第一个因素是账户的密码,而第二个因素区别于传统的密码验证,由于传统的密码验证是由一组静态信息组成,如:字符、图像、手势等,很容易被获取,相对不安全。2FA是基于时间、历史长度、实物(信用卡、SMS手机、令牌、指纹)等自然变量结合一定的加密算法组合出一组动态密码,一般每60秒刷新一次。不容易被获取和破解,相对安全。(取自百度百科)

总结一下,2FA就是在静态密码验证的基础上,再结合一定的自然变量组合出动态密码进行二次验证。

很明显,上述这个现象就是一种2FA的实现。而2FA 系统的其他名称包括OTP(一次性密码)和TOTP(基于时间的一次性密码算法)。
我们这里详细的了解一下TOTP。

C.TOTP

1.什么是TOTP

TOTP(Time-Based One-Time Password Algorithm),是一种基于时间的一次性密码算法,算法的详细说明见:https://www.rfc-editor.org/rfc/rfc6238,其公式表示如下:

// K 代表我们在认证服务器端以及密码生成端(客户设备)之间共享的密钥
// C 表示事件计数的值,8 字节的整数,称为移动因子(moving factor)
// HMAC-SHA-1 表示对共享密钥以及移动因子进行 HMAC 的 SHA1 算法加密,得到 160 位长度(20字节)的哈希结果
// Truncate 表示截断函数 将得到的哈希值进行阶段
// digit 指定动态密码长度,比如我们常见的都是 6 位长度的动态密码
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
PWD(K,C,digit) = HOTP(K,C) mod 10^Digit

TOTP = HOTP(K, T) // T is an integer and represents the number of time steps between the initial counter time T0 and the current Unix time

More specifically, T = (Current Unix time - T0) / X, where the
default floor function is used in the computation.

TOTP算法实际上是基于HOTP(An HMAC-Based One-Time Password Algorithm)(基于事件计数的一次性密码生成算法),只不过把事件计数变成了时间计数。HOTP算法的详细说明见:https://www.rfc-editor.org/rfc/rfc4226。

2.原理详解(基于java-totp项目分析)

从上面的公式大概可以看出,是这么一个流程:

  • 生成一个随机的密钥。
  • 基于时间取一个计数值。通常的做法是用当前的时间除以动态密码的有效时间。
  • 然后将这个计数值和密钥进行哈希运算,并截断到指定的长度。
  • 最后对数位的最大值取余。

有一个java库实现了TOTP算法,我们可以详细分析一下实现的原理: https://github.com/samdjstevens/java-totp。

使用java-totp总共分为两个部分:

  • 1.生成密钥。
  • 2.验证动态密码。

密钥生成部分代码如下:

java totp,【密码专栏】,网络,动态密码,totp算法,密码协议,java
可以看到随机生成了20个字节并且用base32进行编码。

动态密码验证部分的代码如下:

java totp,【密码专栏】,网络,动态密码,totp算法,密码协议,java
java totp,【密码专栏】,网络,动态密码,totp算法,密码协议,java
java totp,【密码专栏】,网络,动态密码,totp算法,密码协议,java

java totp,【密码专栏】,网络,动态密码,totp算法,密码协议,java
大概分为以下几个部分:

  • 1.先取出当前时间所在的桶(30秒一个桶)。
  • 2.在允许的时间误差范围内进行验证。
  • 3.验证的过程先用时间的桶和密钥进行一个哈希运算,然后取低32位(一个整数),最后对10^6取余。

可以看出来,这个实现完美遵循了上面算法的标准,原理也很好理解了。

3.这样真的安全吗

理解了这个算法后,还是有点疑惑,这个算法真的安全吗?

虽然说一个桶内生成的动态密码一定是一样的,但是不同的桶生成的密码一定是不一样的吗?这可不一定,注意到哈希后进行了低32位的截断操作,并且还有对数位的取余操作,其实这两个操作大大提升了密码碰撞的概率。根据生日攻击,是有可能被碰撞出来的。

但是,我们追求的是计算上的安全,在短短的几十秒内碰撞出来的概率,实在是太小了,并且实际应用的时候,如果连续输错几次,可以结合图形验证码进行进一步的验证。

所以,这样的算法在计算上,是非常安全的。

4.常见的支持TOTP的软件

1.Google Authenticator

Google Authenticator毫无疑问是最受欢迎的2FA软件,简洁轻便,无需登录google账号。缺点是数据都存储在本地,换手机的话需要导出数据。

2.Microsoft Authenticator

Microsoft Authenticator 支持工作、学校和非 Microsoft 帐户的多重身份验证。输入密码后,应用提供第二层安全保护。登录时,你将输入密码,然后系统将要求你再用一种方式来证明是你本人。请批准发送至 Microsoft Authenticator 的通知,或输入应用生成的验证码。

https://baike.baidu.com/item/Microsoft%20Authenticator/58322728?fr=aladdin

D.项目实践(基于Java)

我们可以简单的基于java-totp库实现一个用于2FA的验证service。(底层实现这种就没必要重复造轮子了)
先导包:

 <dependency>
 	<groupId>dev.samstevens.totp</groupId>
        <artifactId>totp-spring-boot-starter</artifactId>
     <version>1.7.1</version>
  </dependency>

上代码:


import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.code.DefaultCodeVerifier;
import dev.samstevens.totp.code.HashingAlgorithm;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrDataFactory;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.qr.ZxingPngQrGenerator;
import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.time.SystemTimeProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;


import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;

import static dev.samstevens.totp.util.Utils.getDataUriForImage;

@Service
@Slf4j
public class TOTPVerifyService {

    private final SecretGenerator secretGenerator = new DefaultSecretGenerator();
    private final QrDataFactory qrDataFactory = new QrDataFactory(HashingAlgorithm.SHA1, 6, 30);
    private final QrGenerator qrGenerator = new ZxingPngQrGenerator();
    private final CodeVerifier verifier = new DefaultCodeVerifier(new DefaultCodeGenerator(HashingAlgorithm.SHA1, 6), new SystemTimeProvider());
    private final Map<String, String> uidAndSecret = new HashMap<>();

    public Map<String, String> setupDevice() throws QrGenerationException {
        // 生成 TOTP 密钥
        String secret = secretGenerator.generate();
        QrData data = qrDataFactory.newBuilder().secret(secret).issuer("ATFWUS-TEST").build();

        // 将生成的 TOTP 密钥转换为 Base64 图像字符串
        String qrCodeImage = getDataUriForImage(
                qrGenerator.generate(data),
                qrGenerator.getImageMimeType());

        System.out.println(secret);

        // 返回密钥 和 密钥二维码
        Map<String, String> result = new HashMap<>(2);
        result.put("secret", secret);
        result.put("qrCodeImage", qrCodeImage);
        return result;
    }

    public boolean deviceVerify(String uid, String secret, String code) {
        if (verifier.isValidCode(secret, code)) {
            // 将uid绑定secret并存储
            uidAndSecret.put(uid, secret);
            return true;
        } else {
            return false;
        }
    }

    public void cancelDeviceVerify(String uid) {
        // 取消绑定TOTP密钥
        uidAndSecret.remove(uid);
    }

    public boolean checkCode(String uid, String verifyCode) {
        // 用uid取出绑定的secret
        if(!uidAndSecret.containsKey(uid)) {
            return false;
        }
        String secret = uidAndSecret.get(uid);
        return verifier.isValidCode(secret, verifyCode);
    }

    // test
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        TOTPVerifyService totpVerifyService = new TOTPVerifyService();
        totpVerifyService.setupDevice();
        String uid = "atfwus";
        System.out.println("bind your device!");
        while(true) {
            System.out.println("input secret: ");
            String secret = sc.nextLine();

            System.out.println("input code: ");
            String code = sc.nextLine();

            if(totpVerifyService.deviceVerify(uid, secret, code)) {
                break;
            }
            System.out.println("try again!!!");
        }
        System.out.println("bind device successful!!!");
        System.out.println();
        System.out.println("2FA check!!!");
        while(true) {
            System.out.println("input secret: ");
            String secret = sc.nextLine();

            System.out.println("input code: ");
            String code = sc.nextLine();

            if(totpVerifyService.checkCode(uid, code)) {
                break;
            }
            System.out.println("try again!!!");
        }
        System.out.println("check code pass!!!");
    }
}

将生成的secret复制到手机上,或者扫描生成的二维码即可完成验证。
java totp,【密码专栏】,网络,动态密码,totp算法,密码协议,java

E.总结

连公司vpn的那个软件其实就是通过TOTP的方式进行的验证(也有可能基于其它计数机制,但总的原理不变),这样客户端无需和服务端进行通信,也能实现安全的验证。现实生活中还能找到很多类似的应用例子:像银行的动态密码卡就是一个实例。


参考资料

  • https://www.rfc-editor.org/rfc/rfc6238 TOTP算法详细介绍
  • https://www.rfc-editor.org/rfc/rfc4226 HOTP算法详细介绍
  • https://github.com/samdjstevens/java-totp 一个基于TOTP实现的java库
  • https://baike.baidu.com/item/2FA/14695073?fr=aladdin 2FA介绍
  • https://segmentfault.com/a/1190000008394200 动态密码算法介绍

ATFWUS 2022-12-07文章来源地址https://www.toymoban.com/news/detail-634101.html

到了这里,关于【TOTP】基于时间的动态密码及其工程实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java和c#里的TOTP统一算法

    本文根据 RFC4226 和 RFC6238 文档,详细的介绍 HOTP 和 TOTP 算法的原理和实现。 两步验证已经被广泛应用于各种互联网应用当中,用来提供安全性。对于如何使用两步验证,大家并不陌生,无非是开启两步验证,然后出现一个二维码,使用支持两步验证的移动应用比如 Google Aut

    2024年02月08日
    浏览(37)
  • 基于Verilog 语言开发的FPGA密码锁工程

    基于Verilog 语言开发的FPGA密码锁工程。 通过矩阵键盘输入按键值。 输入12修改密码,13清除密码,可以修改原来默认的密码,修改时首先要输入当前密码进行验证,正确后才能更新当前密码,否则修改不成功。 修改结束后按键15,确认修改成功。 也直接使用默认密码作为最终

    2024年02月10日
    浏览(52)
  • Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单 em

       鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管理的提升提出了更

    2024年02月14日
    浏览(55)
  • Java 版 spring cloud 工程系统管理 工程项目管理系统源码 工程项目各模块及其功能点清单

          工程项目各模块及其功能点清单 一、系统管理     1、数据字典:实现对数据字典标签的增删改查操作     2、编码管理:实现对系统编码的增删改查操作     3、用户管理:管理和查看用户角色     4、菜单管理:实现对系统菜单的增删改查操作     5、角色管理:

    2024年02月15日
    浏览(48)
  • Java 版 spring cloud + spring boot 工程系统管理 工程项目管理系统源码 工程项目各模块及其功能点清单

           工程项目各模块及其功能点清单 一、系统管理     1、数据字典:实现对数据字典标签的增删改查操作     2、编码管理:实现对系统编码的增删改查操作     3、用户管理:管理和查看用户角色     4、菜单管理:实现对系统菜单的增删改查操作     5、角色管理:

    2024年02月14日
    浏览(51)
  • 基于TOTP算法的Github两步验证2FA(双因子)机制Python3.10实现

    从今年(2023)三月份开始,Github开始强制用户开启两步验证2FA(双因子)登录验证,毫无疑问,是出于安全层面的考虑,毕竟Github账号一旦被盗,所有代码仓库都会毁于一旦,关于双因子登录的必要性请参见:别让你的服务器(vps)沦为肉鸡(ssh暴力破解),密钥验证、双向因子登录值

    2024年02月08日
    浏览(53)
  • 最佳实践:基于vite3的monorepo前端工程搭建

    ▪选择理由1:多个应用(可以按业务线产品粒度划分)在同一个repo管理,便于统一管理代码规范、共享工作流 ▪选择理由2:解决跨项目/应用之间物理层面的代码复用,不用通过发布/安装npm包解决共享问题 ▪选择理由1:通过软/硬链接方式,最大程度节省磁盘空间 ▪选择理

    2024年02月06日
    浏览(47)
  • 【毕业设计】58-基于51单片机的智能语音密码锁设计(原理工程+PCB工程+仿真工程+源代码+答辩论文+实物图)

    主要研究内容: 掌握电子密码锁的原理、构造、以及运用51单片机设计出一款简易密码锁,实现相应的一些要求;掌握并且实际运用所学知识,将书中知识转化为实际能力;对密码锁的应用和未来的发展进行分析。 研究方法: 通过相关的书籍和网上资料,查阅相关资料,收集有

    2024年02月05日
    浏览(94)
  • Java中的安全密码散列:最佳实践和代码示例

    在数字安全领域,密码哈希是防止未经授权访问的重要防线。然而,哈希算法的前景已经发生了重大变化,一些方法已经过时,一些更新、更安全的技术正在出现。本文深入研究了为什么像SHA-512这样的传统方法不再适用,加盐和减慢散列过程的重要性,并为现代密码散列技术

    2024年03月27日
    浏览(34)
  • 为大模型工程提效,基于阿里云 ACK 的云原生 AI 工程化实践

    作者:张凯 以 GPT(Generative Pre-trained Transformer)和 Diffusion model 为代表的大语言模型(Large language model,LLM)和生成式人工智能(Generative artificial intelligence,GAI)在过往两年,将人们对 AI 的梦想与期待推向了一个新高峰。这一次,AI 带来的“智能”效果和“涌现”能力,吸引

    2024年01月22日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包