加密与安全_TOTP 一次性密码生成算法

文章目录

  • Pre
  • TOTP是什么
  • TOTP 算法工作原理
  • TOTP 生成公式
  • TOTP 与 HOTP 的对比
  • Code
    • 生成TOTP
    • 验证 TOTP
    • 使用场景
    • 小结
  • TOTP 与 HOTP 的主要区别
  • TOTP 与 HOTP应用场景比较
  • TOTP 与 HOTP安全性分析

在这里插入图片描述

Pre

加密与安全_HTOP 一次性密码生成算法

https://github.com/samdjstevens/java-totp

https://medium.com/@rakesh.open.source/time-based-one-time-password-totp-java-implementation-82a472bd6bf9

https://gist.github.com/rakeshopensource/def80fac825c3e65804e0d080d2fa9a7


TOTP是什么

TOTP (Time-based One-Time Password) 是基于时间的动态密码生成算法,是 HOTP (基于HMAC的一次性密码) 的一个扩展。它的主要区别在于,TOTP 使用当前时间戳作为动态因子,而不是计数器。因此,生成的密码随时间变化,通常在一段时间(如30秒或60秒)内有效。


TOTP 算法工作原理

TOTP 使用当前时间戳与共享的密钥结合,生成一次性密码。以下是 TOTP 生成密码的主要步骤:

  1. 共享密钥:客户端和服务端预先共享一个密钥(通常是 Base32 编码),这和 HOTP 中的密钥是相同的。

  2. 时间戳:TOTP 使用当前时间戳,按时间步长(例如 30 秒)划分成时间段。每个时间段对应一个唯一的密码。

  3. 时间步数:将当前时间戳除以时间步长,得到时间步数。这个步数类似于 HOTP 中的计数器。

  4. HMAC 计算:使用共享的密钥和时间步数,使用 HMAC-SHA1 算法计算出一个哈希值。

  5. 截取密码:从 HMAC 的输出中提取 6 位或 8 位的动态密码。

TOTP 生成公式

TOTP 的生成公式如下:

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

其中:

  • K 是客户端和服务端之间的共享密钥。
  • T 是当前的时间步数,用公式 T = (currentUnixTime - T0) / X 计算。
    • currentUnixTime 是当前时间戳(以秒为单位)。
    • T0 是时间的起始点(一般为0)。
    • X 是时间步长(通常为30秒)。
  • HMAC-SHA-1 使用 K 作为密钥,对时间步数 T 进行 HMAC 计算。
  • Truncate 是截取函数,将 HMAC 的结果截取为 6 位或 8 位的数字。

TOTP 与 HOTP 的对比

  • 时间敏感 vs 计数敏感:TOTP 使用当前时间生成密码,因此密码是时间敏感的,每个密码只有在特定时间段内有效。HOTP 则基于计数器,每个密码对应一个计数器值。
  • 同步问题:TOTP 依赖于时间戳,因此客户端和服务端的时间需要保持同步,而 HOTP 需要计数器同步。

Code

生成TOTP

下面是一个简单的 TOTP 实现,生成 6 位的一次性密码。代码依赖于 javax.crypto.Macjava.security.Key 类来处理 HMAC-SHA1。

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.util.Base32;
import java.util.TimeZone;
import java.time.Instant;
import java.time.Clock;public class TOTP {// TOTP 生成算法public static String generateTOTP(String secret, long time, int digits) throws Exception {// 使用时间步长30秒long timeStep = 30;// 计算时间步数long t = time / timeStep;// 将密钥解码为字节Base32 base32 = new Base32();byte[] key = base32.decode(secret);// 使用 HMAC-SHA1 计算Mac mac = Mac.getInstance("HmacSHA1");SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA1");mac.init(keySpec);// 将时间步数转换为 8 字节的大端字节数组byte[] timeBytes = ByteBuffer.allocate(8).putLong(t).array();// 计算 HMAC 值byte[] hash = mac.doFinal(timeBytes);// 提取动态截取码int offset = hash[hash.length - 1] & 0xf;int binary = ((hash[offset] & 0x7f) << 24)| ((hash[offset + 1] & 0xff) << 16)| ((hash[offset + 2] & 0xff) << 8)| (hash[offset + 3] & 0xff);// 生成 OTP,取二进制数模10^digits,得到指定位数的OTPint otp = binary % (int) Math.pow(10, digits);// 格式化为指定位数return String.format("%0" + digits + "d", otp);}public static void main(String[] args) {try {// 示例密钥(注意:密钥应为 Base32 编码)String secret = "JBSWY3DPEHPK3PXP";// 获取当前 Unix 时间戳long currentTime = Instant.now().getEpochSecond();// 生成 TOTPString otp = generateTOTP(secret, currentTime, 6);System.out.println("生成的 OTP: " + otp);} catch (Exception e) {e.printStackTrace();}}
}

代码解析

  1. 密钥解码:使用 Base32 解码器将字符串密钥解码为字节数组。在 TOTP 中,密钥通常以 Base32 编码存储。
  2. 时间步长:时间步长设为 30 秒,即 TOTP 密码每 30 秒更新一次。
  3. HMAC 计算:使用 HMAC-SHA1 算法,结合密钥和当前时间步数来计算哈希值。
  4. 动态截取:从哈希值中截取出一个 6 位的密码。

验证 TOTP

验证 TOTP 时,客户端和服务端必须在同一时间段内生成相同的密码。为了处理客户端与服务端之间的时间不同步问题,服务端可以允许一个时间窗口范围(例如 ±1 个时间步长)来容错。

为了验证 TOTP,服务端会接收用户输入的 OTP,并根据当前时间戳生成自己的 TOTP,进行比对。如果两者匹配,验证成功,否则失败。为了容错,服务端通常会允许一定的时间窗口来处理客户端和服务端之间可能存在的轻微时间不同步问题。

TOTP 验证流程:

  1. 当前时间戳计算:服务端根据当前时间戳生成 TOTP。
  2. 时间窗口:为了容错,服务端可以生成多个不同时间步内的 TOTP,通常是当前时间步及前后时间步。用户输入的 OTP 与服务端生成的这些 OTP 进行匹配。
  3. 密钥共享:TOTP 的核心是基于一个共享的密钥,客户端和服务端都必须使用同样的密钥生成 OTP。

验证TOTP的Java代码

package com.artisan.otp.totp;import org.apache.commons.codec.binary.Base32;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.time.Instant;/*** @author 小工匠* @version 1.0* @date 2024/10/2 9:37* @mark: show me the code , change the world*/public class TOTPValidator {/*** TOTP 生成算法(和生成 OTP 的算法一致)** @param secret* @param time* @param digits* @return* @throws Exception*/public static String generateTOTP(String secret, long time, int digits) throws Exception {// 时间步长,通常为30秒long timeStep = 30;// 将时间转换为时间步数long t = time / timeStep;// Base32 解码密钥Base32 base32 = new Base32();byte[] key = base32.decode(secret);// 使用 HMAC-SHA1Mac mac = Mac.getInstance("HmacSHA1");SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA1");mac.init(keySpec);// 将时间步数转换为8字节的大端字节数组byte[] timeBytes = ByteBuffer.allocate(8).putLong(t).array();// 生成 HMAC 哈希值byte[] hash = mac.doFinal(timeBytes);// 提取动态截取码int offset = hash[hash.length - 1] & 0xf;int binary = ((hash[offset] & 0x7f) << 24)| ((hash[offset + 1] & 0xff) << 16)| ((hash[offset + 2] & 0xff) << 8)| (hash[offset + 3] & 0xff);// 生成指定位数的 OTPint otp = binary % (int) Math.pow(10, digits);// 格式化 OTP,确保是6位return String.format("%0" + digits + "d", otp);}/*** TOTP 验证算法* @param secret* @param inputOTP* @param window* @param digits* @return* @throws Exception*/public static boolean validateTOTP(String secret, String inputOTP, int window, int digits) throws Exception {// 当前时间戳long currentTime = Instant.now().getEpochSecond();// 在前后时间步长的窗口内验证for (int i = -window; i <= window; i++) {String generatedOTP = generateTOTP(secret, currentTime + (i * 30), digits);System.out.println("Generated OTP: " + generatedOTP);if (generatedOTP.equals(inputOTP)) {// 验证成功return true;}}// 验证失败return false;}public static void main(String[] args) {try {// 示例密钥(注意:应为 Base32 编码)String secret = "JBSWY3DPEHPK3PXP";// 假设用户输入的 OTP(通常由客户端生成的 TOTP)// 示例 OTP,需要实际生成 TOTPString inputOTP = "306461";// 获取当前 Unix 时间戳long currentTime = Instant.now().getEpochSecond();// 生成 TOTPinputOTP = generateTOTP(secret, currentTime, 6);System.out.println("inputOTP: " +inputOTP);// 验证 OTP    容错窗口为1,6位OTPboolean isValid = validateTOTP(secret, inputOTP, 1, 6);if (isValid) {System.out.println("OTP 验证成功!");} else {System.out.println("OTP 验证失败!");}} catch (Exception e) {e.printStackTrace();}}
}

代码说明

  1. generateTOTP: 生成 TOTP 的算法,使用当前时间戳和共享密钥生成一次性密码。
  2. validateTOTP: 验证用户输入的 OTP 是否有效。此函数允许一个时间窗口(window),比如前后 30 秒范围内的 OTP 都可接受。默认 6 位 OTP。
  3. Base32 解码: Base32 编码用于解码密钥,因为密钥通常以 Base32 编码形式存储。
  4. 时间步长: TOTP 的时间步长通常是 30 秒。代码中以当前时间为基准,根据时间步长计算时间步数,生成 OTP。
  5. 容错窗口: 允许服务端生成当前时间步以及前后若干步内的 OTP,防止客户端与服务端时间不同步。

如何使用

  1. 密钥一致: 确保客户端和服务端使用同一个共享密钥。密钥通常是 Base32 编码的字符串。
  2. 验证 OTP: 服务端使用当前时间步生成 TOTP,并与用户输入的 OTP 进行比对。如果在设定的时间窗口内有匹配的 OTP,验证成功。

调试步骤

  1. 打印调试信息: 在生成和验证过程中打印 OTP 和相关的时间步,帮助确认时间步数和生成的 OTP 是否一致。
  2. 调整时间窗口: 如果客户端和服务端的时间差异较大,尝试增加 window 的大小,允许更大的容错范围。
    在这里插入图片描述

这个实现适用于 TOTP 的典型应用场景,例如双因素认证 (2FA)【基于时间的一次性密码生成和验证】。

使用场景

  • 双因素认证 (2FA):TOTP 是常见的 2FA 算法之一,用户使用手机中的身份验证器(如 Google Authenticator)生成一次性密码进行登录验证。
  • 高安全性系统:银行、电子商务网站等需要额外的安全措施,使用 TOTP 来防止密码泄露和账户被劫持。

小结

TOTP 是一种基于时间的动态密码算法,通过时间戳和共享密钥生成一次性密码,常用于双因素身份验证场景。相比于 HOTP,TOTP 不需要计数器同步,使用更加便捷,但要求客户端和服务端的时间同步。


TOTP 与 HOTP 的主要区别

特点HOTPTOTP
依赖性计数器时间戳
密码有效性永久有效,直到被使用短时间内有效(30 或 60 秒)
生成方式每次生成后计数器递增每个时间周期内自动生成
安全性密码可能长期有效,安全性较低动态更新,安全性更高
适用场景适用于基于事件的认证系统适用于二次身份验证系统

TOTP 与 HOTP应用场景比较

  • HOTP 适用场景

    • 基于事件触发的认证系统:每次用户请求认证时,系统会递增计数器生成密码。这类系统适用于需要物理令牌或硬件设备的场景(如早期银行安全令牌)。
    • 设备或网络不稳定环境:由于 HOTP 不依赖时间,客户端和服务器的时间不同步问题不会影响认证。
  • TOTP 适用场景

    • 二次身份验证(2FA):TOTP 在大多数现代的二次身份验证系统中使用,如 Google Authenticator、Microsoft Authenticator 等。用户每 30 秒生成一个新密码,确保密码及时失效。
    • 需要更高安全性和频繁登录的系统:由于 TOTP 密码动态更新且过期较快,适合频繁使用和安全性要求较高的应用。

TOTP 与 HOTP安全性分析

  • HOTP 的安全性弱点:由于 HOTP 密码在未使用前一直有效,攻击者可以通过拦截未被使用的密码进行重放攻击,或暴力猜测计数器值。
  • TOTP 的安全优势:TOTP 基于时间戳生成,密码的有效期较短(通常 30 秒)。即使攻击者截获密码,也很难在其
    过期前使用,因此 TOTP 提供了更强的安全性。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/880876.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gdb 调试 linux 应用程序的技巧介绍

使用 gdb 来调试 Linux 应用程序时&#xff0c;可以显著提高开发和调试的效率。gdb&#xff08;GNU 调试器&#xff09;是一款功能强大的调试工具&#xff0c;适用于调试各类 C、C 程序。它允许我们在运行程序时检查其状态&#xff0c;设置断点&#xff0c;跟踪变量值的变化&am…

指针 (5)

目录 1. 字符指针变量 2. 数组指针变量 3. ⼆维数组传参的本质 4. 函数指针变量 5.typedef 关键字 6 函数指针数组 7.转移表 计算器的⼀般实现 1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针 char* #include <stdio.h> int main() {char* ch …

VB.net读写NDEF标签URI智能海报WIFI蓝牙连接

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 Public Class Form1Dim oldpicckey(0 To 5) As Byte 卡片旧密码Dim newpicckey(0 To 5) As Byte 卡片新密码Function GetTagUID() As StringDim status As ByteDim myctrlword As …

矩阵系统源码搭建的具体步骤,支持oem,源码搭建

一、前期准备 明确需求 确定矩阵系统的具体用途&#xff0c;例如是用于社交媒体管理、电商营销还是其他领域。梳理所需的功能模块&#xff0c;如多账号管理、内容发布、数据分析等。 技术选型 选择适合的编程语言&#xff0c;如 Python、Java、Node.js 等。确定数据库类型&…

Activiti7 工作流引擎学习

目录 一. 什么是 Activiti 工作流引擎 二. Activiti 流程创建步骤 三. Activiti 数据库表含义 四. BPMN 建模语言 五. Activiti 使用步骤 六. 流程定义与流程实例 一. 什么是 Activiti 工作流引擎 Activiti 是一个开源的工作流引擎&#xff0c;用于业务流程管理&#xf…

Linux开发讲课45--- 链表

Linux内核代码中广泛使用了数据结构和算法,其中最常用的有链表、队列kfifo、红黑树、基数树和位图。 链表 Linux内核代码大量使用了链表这种数据结构。链表是在解决数组不能动态扩展这个缺陷而产生的一种数据结构。 链表所包含的元素可以动态创建并插入和删除。链表的每个元素…

【经典机器学习算法】谱聚类算法及其实现(python)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. 前…

躺平成长:微信小程序运营日记第二天

在进行属于生活的开源之后&#xff0c;自己更加感受到自己存在的渺茫&#xff0c;同时更加开始深刻领会&#xff0c;开源的重要性&#xff0c;在开源&#xff0c;开放&#xff0c;创造&#xff0c;再创新的思维模式下&#xff0c;不发布八部金刚功相关的训练视频&#xff0c;自…

每日一题|983. 最低票价|动态规划、记忆化递归

本题求解最小值&#xff0c;思路是动态规划&#xff0c;但是遇到的问题是&#xff1a;动态规划更新的顺序和步长&#xff0c;以及可能存在的递归溢出问题。 1、确定dp数组含义 dp[i]表示第i天到最后一天&#xff08;可能不在需要出行的天数里&#xff09;&#xff0c;需要花费…

Suricata:开源网络分析和威胁检测

Suricata 是一款高性能、开源网络分析和威胁检测软件&#xff0c;被大多数私人和公共组织使用&#xff0c;并被主要供应商嵌入以保护他们的资产。 Suricata 功能 Suricata 提供全面的网络安全监控 (NSM) 功能&#xff0c;包括记录 HTTP 请求、捕获和存储 TLS 证书以及从网络流…

汽车3d动画渲染选择哪个?选择最佳云渲染解决方案

面临汽车3D动画渲染挑战&#xff1f;选择正确的云渲染服务至关重要。探索最佳解决方案&#xff0c;优化渲染效率&#xff0c;快速呈现逼真动画。 汽车3d动画渲染选择哪个&#xff1f; 对于汽车3D动画渲染&#xff0c;选择哪个渲染器取决于你的项目需求、预算和期望的效果。Ble…

yolov8/9/10模型在安全帽、安全衣检测中的应用【代码+数据集+python环境+GUI系统】

yolov8910模型安全帽、安全衣检测中的应用【代码数据集python环境GUI系统】 yolov8/9/10模型在安全帽、安全衣检测中的应用【代码数据集python环境GUI系统】 背景意义 安全帽和安全衣在工业生产、建筑施工等高风险作业环境中是保护工人免受意外伤害的重要装备。然而&#xff0…

Qt 学习第十一天:QTableWidget 的使用

一、创建QTableWidget对象&#xff0c;设置大小&#xff0c;在窗口的位置 //创建tablewidgetQTableWidget *table new QTableWidget(this);table->resize(550, 300);table->move(100, 100); //移动 二、设置表头 //设置表头QStringList headerList; //定义headerList…

web开发(1)-基础

这是对b站课程的总结&#xff0c;后续可能会继续更 01 前后端分离介绍_哔哩哔哩_bilibili01 前后端分离介绍是Web应用开发-后端基础-基于Springboot框架的第1集视频&#xff0c;该合集共计29集&#xff0c;视频收藏或关注UP主&#xff0c;及时了解更多相关视频内容。https://w…

GPG error golang 1.19

1. 问题描述及原因分析 在飞腾2000的服务器&#xff0c;OS为Kylin Linux Advanced Server release V10环境下&#xff0c;docker版本为18.09.0&#xff08;docker-engine-18.09.0-101.ky10.aarch64&#xff09;&#xff0c;基于容器镜像golang:1.19编译新的容器镜像&#xff0…

【C++篇】启航——初识C++(上篇)

下篇&#xff1a;【C篇】启航——初识C&#xff08;下篇&#xff09; 目录 引言 一、C的起源和发展史 1.起源 2.C版本更新 二、C在⼯作领域中的应⽤ 三、C入门建议 1.参考文档 2.推荐书籍 四、C的第一个程序 1.C语言写法 2.C写法 五、命名空间 1.为什么要有命名空…

AI 对话工具汇总

&#x1f423;个人主页 可惜已不在 &#x1f424;这篇在这个专栏AI_可惜已不在的博客-CSDN博客 &#x1f425;有用的话就留下一个三连吧&#x1f63c; 目录 前言: 正文: 前言: 在科技飞速发展的时代&#xff0c;AI 对话正逐渐成为我们获取信息、交流思想的新方式。它以强…

若无向图G(V,E)中含7个顶点,为保证图G在任何情况下都是连通的,则需要的边数最少是多少?

这乍一看是不是可抽象&#xff08;迷糊&#xff09;了&#xff0c;butttt待我小翻译一下。 先举少一点的例子&#xff0c;假如我们有三个点&#xff0c;我给你两条边&#xff0c;那是不是不管咋连都一定一定是连通的。 那我们再进一步&#xff0c;假如四个点呢&#xff1f;我给…

RabbitMQ 界面管理说明

1.RabbitMQ界面访问端口和后端代码连接端口不一样 界面端口是15672 http://localhost:15672/ 后端端口是 5672 默认账户密码登录 guest 2.总览图 3.RabbitMq数据存储位置 4.队列 4.客户端消费者连接状态 5.队列运行状态 6.整体运行状态

在Linux中将设备驱动的地址映射到用户空间

本期主题&#xff1a; MMU的简单介绍&#xff0c;以及如何实现设备地址映射到用户空间 往期链接&#xff1a; Linux内核链表零长度数组的使用inline的作用嵌入式C基础——ARRAY_SIZE使用以及踩坑分析Linux下如何操作寄存器&#xff08;用户空间、内核空间方法讲解&#xff09;…