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

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

最大公因数:欧几里得算法

简述         求两个数字 m和n 的最大公因数,假设r是m%n的余数,只要n不等于0,就一直执行 m=n,n=r 举例 以18和12为例 m n r18 % 12 = 612 % 6 = 06 0所以最大公因数为:6 代码实现 #include<iostream>using namespace std;/