Google Authenticator 算法

2023-10-17 12:45
文章标签 算法 google authenticator

本文主要是介绍Google Authenticator 算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Google Authenticator 算法

  • 目录
    • 概述
      • 需求:
    • 设计思路
    • 实现思路分析
      • 1.Shared Secret(共享密钥)
      • 2.2. Input(Current Time)
      • 3. Signing Function(签名函数)
    • 解决的代码:
  • 参考资料和推荐阅读

Survive by day and develop by night.
talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive.
happy for hardess to solve denpendies.

目录

概述

此项服务所使用的算法已列于RFC 6238和RFC 4226中。谷歌验证器上的动态密码按照时间或使用次数不断动态变化(默认30秒变更一次)。

需求:

设计思路

此项服务所使用的算法已列于RFC 6238和RFC 4226中。谷歌验证器上的动态密码按照时间或使用次数不断动态变化(默认30秒变更一次)。
RFC 6238 is a standard that specifies the Time-Based One-Time Password (TOTP) algorithm, which is used for two-factor authentication. The TOTP algorithm is based on a shared secret between the user and the service provider, and a time-based counter that generates a unique, one-time password that is valid for a short period of time. This mechanism provides an additional layer of security to protect against unauthorized access to sensitive data or systems. The RFC outlines the requirements and specifications for implementing TOTP, including the format of the shared secret, the algorithm for generating the one-time password, and the synchronization of the system’s clock. The standard has been widely adopted by service providers and is used in many popular two-factor authentication schemes.

实现思路分析

1.Shared Secret(共享密钥)

用户开启双因子(2FA)验证后,服务器会生成一个密钥(secret);
服务器提示用户扫描二维码或者手动输入的方式,将密钥保存在用户的手机上。此时,用户和服务器现在都拥有同一把钥匙。
用户登录时,手机客户端Authy使用这个密钥和当前的Unix时间戳,生成一个hash value(h1),有效期默认为30s。用户在有效期内,将这个哈希值提交给服务器;
服务器也使用密钥和当前时间戳,生成一个hash value(h2),将h2和用户提交的h1进行比较,如果两者一致,就能够正常登陆,否则,拒绝登陆。

2.2. Input(Current Time)

因为从算法原理来讲,身份验证服务器会基于同样的时间来重复进行用户手机的运算。进一步来说,服务器会计算当前时间前后几分钟内的令牌,跟用户提交的令牌比较。所以如果时间上相差太多,身份验证过程就会失败。

3. Signing Function(签名函数)

签名函数使用的是HMAC-SHA1。HMAC是基于哈希的消息验证码,能够用安全的单向哈希函数(SHA1)来生成签名。
验证算法的原理:只有共享密钥拥有者和服务器才能根据同样的输入(基于时间的)得到同样的输出签名。
hmac = SHA1(secret + SHA1(secret + input))

  1. 首先,使用base32的解码密钥
    secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))

input = CURRENT_UNIX_TIME()
input = CURRENT_UNIX_TIME() / 30
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))
input = CURRENT_UNIX_TIME() / 30
hmac = SHA1(secret + SHA1(secret + input))

三、具体实现(Java)

解决的代码:

	<!--二维码的生成工具类-->
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version>
</dependency><!--谷歌身份验证器-->
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.12</version>
</dependency>
 import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import org.apache.commons.codec.binary.Base32;import org.apache.commons.codec.binary.Base64;/*** @Description: 谷歌身份验证器工具类*/public class GoogleAuthenticator {/*** 生成秘钥的长度*/public static final int SECRET_SIZE = 10;public static final String SEED = "g8GjEvTbW5oVSV7avL47357438reyhreyuryetredLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx";/*** 实现随机数算法*/public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";/*** 时间前后偏移量* 用于防止客户端时间不精确导致生成的TOTP与服务器端的TOTP一直不一致* 如果为0,当前时间为 10:10:15* 则表明在 10:10:00-10:10:30 之间生成的TOTP 能校验通过* 如果为1,则表明在* 10:09:30-10:10:00* 10:10:00-10:10:30* 10:10:30-10:11:00 之间生成的TOTP 能校验通过* 以此类推*/int window_size = 10; // default 3 - max 17/*** set the windows size. This is an integer value representing the number of* 30 second windows we allow The bigger the window, the more tolerant of* clock skew we are.** @param s*            window size - must be >=1 and <=17. Other values are ignored*/public void setWindowSize(int s) {if (s >= 1 && s <= 17)window_size = s;}/*** 生成随机密钥,每个用户独享一份密钥* @return secret key*/public static String generateSecretKey() {SecureRandom sr = null;try {sr = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM);sr.setSeed(Base64.decodeBase64(SEED.getBytes()));byte[] buffer = sr.generateSeed(SECRET_SIZE);Base32 codec = new Base32();byte[] bEncodedKey = codec.encode(buffer);return new String(bEncodedKey);} catch (NoSuchAlgorithmException e) {// should never occur... configuration error}return null;}/*** 生成一个google身份验证器,识别的字符串,只需要把该方法返回值生成二维码扫描就可以了。* 最后展示的账户名称将会是 label:user* @param label 标签* @param user 账号* @param secret 密钥* @return*/public static String getQRBarcode(String label, String user, String secret) {String format = "otpauth://totp/%s:%s?secret=%s";return String.format(format, label, user, secret);}/*** 生成一个google身份验证器,识别的字符串,只需要把该方法返回值生成二维码扫描就可以了。*最后展示的账户名称将会是 user* @param user 账号* @param secret 密钥* @return*/public static String getQRBarcode(String user, String secret) {String format = "otpauth://totp/%s?secret=%s";return String.format(format, user, secret);}/*** 验证code是否合法* @param secret 秘钥* @param code 验证码* @param timeMses 时间戳* @return true表示正确 false 表示错误*/public  boolean check_code(String secret, long code, long timeMses) {if(secret == null || "".equals(secret)){return false;}Base32 codec = new Base32();byte[] decodedKey = codec.decode(secret);// convert unix msec time into a 30 second "window"// this is per the TOTP spec (see the RFC for details)long t = (timeMses / 1000L) / 30L;// Window is used to check codes generated in the near past.// You can use this value to tune how far you're willing to go.for (int i = -window_size; i <= window_size; ++i) {long hash;try {hash = verify_code(decodedKey, t + i);} catch (Exception e) {// Yes, this is bad form - but// the exceptions thrown would be rare and a static// configuration probleme.printStackTrace();throw new RuntimeException(e.getMessage());// return false;}if (hash == code) {return true;}}// The validation code is invalid.return false;}/*** 根据时间偏移量计算** @param key* @param t* @return* @throws NoSuchAlgorithmException* @throws InvalidKeyException*/private static int verify_code(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException {byte[] data = new byte[8];long value = t;for (int i = 8; i-- > 0; value >>>= 8) {data[i] = (byte) value;}SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");Mac mac = Mac.getInstance("HmacSHA1");mac.init(signKey);byte[] hash = mac.doFinal(data);int offset = hash[20 - 1] & 0xF;// We're using a long because Java hasn't got unsigned int.long truncatedHash = 0;for (int i = 0; i < 4; ++i) {truncatedHash <<= 8;// We are dealing with signed bytes:// we just keep the first byte.truncatedHash |= (hash[offset + i] & 0xFF);}truncatedHash &= 0x7FFFFFFF;truncatedHash %= 1000000;return (int) truncatedHash;}}

参考资料和推荐阅读

参考资料
官方文档
开源社区
博客文章
书籍推荐

  1. https://www.cnblogs.com/Fogram-c/p/16978939.html

欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!同时,期望各位大佬的批评指正~

这篇关于Google Authenticator 算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/225500

相关文章

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

大林 PID 算法

Dahlin PID算法是一种用于控制和调节系统的比例积分延迟算法。以下是一个简单的C语言实现示例: #include <stdio.h>// DALIN PID 结构体定义typedef struct {float SetPoint; // 设定点float Proportion; // 比例float Integral; // 积分float Derivative; // 微分flo

LeetCode 算法:二叉树的中序遍历 c++

原题链接🔗:二叉树的中序遍历 难度:简单⭐️ 题目 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1] 输出:[1] 提示: 树中节点数目在范围 [0, 100] 内 -100 <= Node.

【Java算法】滑动窗口 下

​ ​    🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🦌一.水果成篮 题目链接:904.水果成篮 ​ 算法原理 算法原理是使用“滑动窗口”(Sliding Window)策略,结合哈希表(Map)来高效地统计窗口内不同水果的种类数量。以下是详细分析: 初始化:创建一个空的哈希表 map 用来存储每种水果的数量,初始化左右指针 left

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插

算法与数据结构面试宝典——回溯算法详解(C#,C++)

文章目录 1. 回溯算法的定义及应用场景2. 回溯算法的基本思想3. 递推关系式与回溯算法的建立4. 状态转移方法5. 边界条件与结束条件6. 算法的具体实现过程7. 回溯算法在C#,C++中的实际应用案例C#示例C++示例 8. 总结回溯算法的主要特点与应用价值 回溯算法是一种通过尝试各种可能的组合来找到所有解的算法。这种算法通常用于解决组合问题,如排列、组合、棋盘游

【图像识别系统】昆虫识别Python+卷积神经网络算法+人工智能+深度学习+机器学习+TensorFlow+ResNet50

一、介绍 昆虫识别系统,使用Python作为主要开发语言。通过TensorFlow搭建ResNet50卷积神经网络算法(CNN)模型。通过对10种常见的昆虫图片数据集(‘蜜蜂’, ‘甲虫’, ‘蝴蝶’, ‘蝉’, ‘蜻蜓’, ‘蚱蜢’, ‘蛾’, ‘蝎子’, ‘蜗牛’, ‘蜘蛛’)进行训练,得到一个识别精度较高的H5格式模型文件,然后使用Django搭建Web网页端可视化操作界面,实现用户上传一

【数据结构与算法 经典例题】使用队列实现栈(图文详解)

💓 博客主页:倔强的石头的CSDN主页               📝Gitee主页:倔强的石头的gitee主页    ⏩ 文章专栏:《数据结构与算法 经典例题》C语言                                   期待您的关注 ​​ 目录  一、问题描述 二、前置知识 三、解题思路 四、C语言实现代码 🍃队列实现代码:

算法11—判断一个树是不是二叉查询树

问题: 给定一个二叉树,判断它是否是二叉查询树。 思路: 要判断是否是二叉查询树,标准就是看每一个节点是否满足:1、左节点及以下节点的值比它小;2、右节点及以下节点的值比它大。当然,前提是子节点都存在的情况。所以,我们需要从根节点不断向下递归,只要所有节点都满足,那么就是BST,否则,就不是。 代码: [java]  view plain copy pri