加密与安全_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,得到指定位数的OTP
        int 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();

            // 生成 TOTP
            String 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-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
        int 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,需要实际生成 TOTP
            String inputOTP = "306461";

            // 获取当前 Unix 时间戳
            long currentTime = Instant.now().getEpochSecond();

            // 生成 TOTP
            inputOTP = generateTOTP(secret, currentTime, 6);
            System.out.println("inputOTP: " +inputOTP);

            // 验证 OTP    容错窗口为1,6位OTP
            boolean 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 提供了更强的安全性。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot vue应急物资供应管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php python(flask Django) 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找…

剖解最小栈

最小栈 思路&#xff1a; 1. 首先实例化两个栈&#xff0c;分别是stack用于存放数据&#xff0c;minstack用于存放最小值 2. 将第一个元素压入两个栈中&#xff0c;判断此时若minStack栈中为空&#xff0c;则表示压入的为第一个数据 if ( minStack.empty () ) { minStack.pus…

【GT240X】【04】你必须知道的 50 多个 Linux 命令

文章目录 一、介绍二、五十个linux命令一览表三、50个命令详解四、结论 你必须知道的 50 多个 Linux 命令 一、介绍 你经常使用 Linux 命令&#xff1f;今天&#xff0c;我们将介绍 50 多个你必须知道的 Linux 命令。下面列出的命令是一些最有用和最常用的 Linux 命令&#x…

IDEA 最新版创建 Sping Boot 项目没有 JDK8 选项的解决方案

问题 今天新建一个 Java 项目写 demo 时&#xff0c;发现 Idea 上只能勾选 Java 17、21、23 三个版本 解决方案 IDEA 页面创建 Spring 项目&#xff0c;其实是访问 spring initializr 去创建项目。我们可以通过阿里云国服去间接创建 Spring 项目。服务器 URL 地址替换为 ht…

蓝桥杯【物联网】零基础到国奖之路:十四. 扩展模块之温湿度传感器

蓝桥杯【物联网】零基础到国奖之路:十四. 扩展模块之温湿度传感器 第一节 硬件解读第二节 CubeMX配置第三节 模版代码 第一节 硬件解读 STS3x-DIS是sensirion新一代温湿度传感器。精度较高&#xff0c;速度较快。SHT3x内部集成了湿度传感器和温度传感器&#xff0c;ADC采样输入…

[网络]抓包工具介绍 tcpdump

一、tcpdump tcpdump是一款基于命令行的网络抓包工具&#xff0c;可以捕获并分析传输到和从网络接口流入和流出的数据包。 1.1 安装 tcpdump 通常已经预装在大多数 Linux 发行版中。如果没有安装&#xff0c;可以使用包管理器 进行安装。例如 Ubuntu&#xff0c;可以使用以下…

9-贪心算法

参考&#xff1a;代码随想录 题目分类大纲如下&#xff1a; 贪心算法理论基础 什么是贪心&#xff1f; 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心的套路&#xff08;什么时候用贪心&#xff09; 贪心算法并没有固定的套路&#xff0c;说白了…

OpenSource - 开源WAF_SamWaf

文章目录 PreSafeLine VS SamWaf开发初衷软件介绍架构界面主要功能 使用说明下载最新版本快速启动WindowsLinuxDocker 启动访问升级指南自动升级手动升级 在线文档 代码相关代码托管介绍和编译已测试支持的平台测试效果 安全策略问题反馈许可证书贡献代码 Pre Nginx - 集成Mod…

Java继承、final/protected说明、super/this辨析

目录 1.什么是继承 2.继承的特征 3.子类构造方法 4.super和this辨析 5.再谈初始化 6.protected关键字用法说明 7.final的用法说明 1.什么是继承 上面的这个animal就是基类&#xff0c;我们的这个dog和bird都是继承这个基类的特征&#xff0c;使用的是extends这个关键字&a…

Python编写的贪吃蛇小游戏

安装包 pip install pygame完整代码 import pygame import randompygame.init()# 定义颜色 white (255, 255, 255) black (0, 0, 0) red (213, 50, 80) green (0, 255, 0) blue (50, 153, 213)# 定义屏幕大小 dis_width 800 dis_height 600dis pygame.display.set_mo…

【大数据入门 | Hive】函数{单行函数,集合函数,炸裂函数,窗口函数}

1. 函数简介&#xff1a; Hive会将常用的逻辑封装成函数给用户进行使用&#xff0c;类似于Java中的函数。 好处&#xff1a;避免用户反复写逻辑&#xff0c;可以直接拿来使用。 重点&#xff1a;用户需要知道函数叫什么&#xff0c;能做什么。 Hive提供了大量的内置函数&am…

Redis操作常用API

说明&#xff1a;Redis应用于java项目中&#xff0c;操作Redis数据可以使用API&#xff0c;相较于命令行更方便。使用前&#xff0c;需先添加依赖。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-re…

云栖实录 | 开源大数据全面升级:Native 核心引擎、Serverless 化、湖仓架构引领云上大数据发展

本文根据2024云栖大会实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a; 王 峰 | 阿里云智能集团研究员、开源大数据平台负责人 李 钰&#xff5c;阿里云智能集团资深技术专家 范 振&#xff5c;阿里云智能集团高级技术专家 李劲松&#xff5c;阿里云…

【机器学习基础】Transformer学习

Transformer学习 一、输入1. Word Embedding2. Positional EncodingPositional Encoding的计算方法二、自注意力机制二、Add & Norm层1. Add 代表残差连接(Residual Connection)2. Norm= Normalization归一化三、FeedForward层其他资料一、输入 第一步:获取输入句子的每…

基于微信小程序的四六级词汇+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;四六级词汇小程序被用户普遍使用&#xff0c;为方便用户能…

银河麒麟V10 SP1如何进入救援模式?

银河麒麟V10 SP1如何进入救援模式&#xff1f; 1、准备工作2、进入BIOS/UEFI进入救援模式注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在使用银河麒麟高级服务器操作系统V10 SP1时&#xff0c;如果遇到系统无法正常启动或需要进…

搭建基于H.265编码的RTSP推流云服务器

一、前言 网上能够找到的RTSP流地址&#xff0c;均是基于H.264编码的RTSP流地址&#xff0c;无法测试应用是否可以播放H265实时流为此&#xff0c;搭建本地的把H.264转码成H.265的RTSP服务器&#xff0c;不管是通过VLC搭建本地RTSP服务器&#xff0c;还是通过FFmpeg搭建本地RT…

关于HTML 案例_个人简历展示01

案例效果展示 代码 <!DOCTYPE html> <lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>个人简历信息</title> </he…

win11/win10/windows下快安装并使用git

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Git 的特点&#xff1f;二、GIT安装方法1.打开GIT官网2.下载git安装程序整个安装过程基本上直接用默认选项就可以 总结 前言 提示&#xff1a;GIT介绍 GI…

【环境配置】科研小白Windows下安装Git

2024年小白使用Win10安装Git 2.46.2教程&#xff1a; 1 下载安装包 访问下载地址 Git - Downloading Package (git-scm.com) 下载之后打开文件 2 安装过程 点击Next 2.1 选择安装路径 2.2 选择勾选必要组件 2.3 一路Next 这一步直接Next即可 继续点击Next 继续点击Ne…