• 关于Google身份验证器、基于时间的一次性密码 (TOTP)算法的初步了解


    一、Google Authenticator

    1、概述

            Google Authenticator是基于双因素身份验证 ( 2FA ) 的应用程序,有助于识别用户身份并确认用户声称自己是谁以及他是否真的是这个人。

            当您启用两步验证(也称为双重身份验证)时,您会为您的帐户添加额外的安全层。您使用您知道的信息(您的密码)和您拥有的信息(发送到您手机的代码)登录。

            双重身份验证降低了入侵者伪装成授权用户的可能性。身份验证因素是用于验证某人或某物是否是他们所宣称的身份或身份的凭证类别。分为三类: 知识因素是用户知道的凭证,通常是用户名和密码;占有因素是用户拥有的东西,通常是手机;固有因素是用户的事物,通常是生物特征,例如指纹或虹膜图案。

            与基于 SMS 的验证相比,使用 2FA 的优势在于,您的移动 sim 卡可能会使用您的社会安全号码被盗到新手机上,或者由于安全漏洞,短信可能会通过您的网络提供商被拦截。2FA 消除了这种可能性,并且完全取决于在其上注册的设备,而与网络提供商无关。

    2、2FA

            双重身份验证( 2FA ) 是一种为您的帐户增加额外安全性的方法。第一个“因素”是任何帐户的标准密码。第二个“因素”是从移动设备或计算机上的应用程序检索到的验证码。

            2FA 在概念上类似于某些国家/地区的银行在线银行所需的安全令牌设备。就是我们日常用到的银行的USB-KEY。 

            2FA 系统的其他名称包括OTP(一次性密码)和TOTP(基于时间的一次性密码算法)。

    3、工作原理

            身份验证器适用于任何启用了双因素身份验证的站点或服务。与大多数基于 Web 的 2FA 应用程序一样,该系统结合了知识和拥有功能。要访问网站或基于 Web 的服务,用户需要输入其正常的用户名和密码,然后输入由登录触发的一次性密码 ( OTP ),该密码会发送到他的设备。该组合可验证在网站上输入登录数据的同一个人是否拥有下载 Google Authenticator 应用程序的设备。

            密码可能很容易被破解或以其他方式窃取,但由于绝大多数攻击都是通过 Internet 进行的,因此黑客不太可能也可以访问用户的物理设备。

            Authenticator 应用程序基于IETF 的RFC 6238 文档中指定的基于时间的一次性密码 ( TOTP ) 系统。TOTP算法生成一个六位数的密码,该密码将当前时间考虑在内,以确保每个密码都是唯一的。密码每 30-60 秒更改一次,以提高安全性。

    二、什么是TOTP

    1、概述

            英文是Time-Based One-Time Password Algorithm,HOTP 算法基于HMAC-SHA-1 算法。

            关于详细算法可以查看下面文档,可能需要科学上网。

    RFC 6238: TOTP: Time-Based One-Time Password Algorithmhttps://www.rfc-editor.org/rfc/rfc6238

    2、Java参考代码

    1. import java.lang.reflect.UndeclaredThrowableException;
    2. import java.security.GeneralSecurityException;
    3. import java.text.DateFormat;
    4. import java.text.SimpleDateFormat;
    5. import java.util.Date;
    6. import javax.crypto.Mac;
    7. import javax.crypto.spec.SecretKeySpec;
    8. import java.math.BigInteger;
    9. import java.util.TimeZone;
    10. /**
    11. * 这是 OATH TOTP 算法的示例实现。
    12. * Visit www.openauthentication.org for more information.
    13. */
    14. public class TOTP {
    15. private TOTP() {}
    16. /**
    17. * 此方法使用 JCE 提供加密算法。 HMAC 使用加密散列算法作为参数计算散列消息验证码。
    18. *
    19. * @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256,
    20. * HmacSHA512)
    21. * @param keyBytes: the bytes to use for the HMAC key
    22. * @param text: the message or text to be authenticated
    23. */
    24. private static byte[] hmac_sha(String crypto, byte[] keyBytes,
    25. byte[] text){
    26. try {
    27. Mac hmac;
    28. hmac = Mac.getInstance(crypto);
    29. SecretKeySpec macKey =
    30. new SecretKeySpec(keyBytes, "RAW");
    31. hmac.init(macKey);
    32. return hmac.doFinal(text);
    33. } catch (GeneralSecurityException gse) {
    34. throw new UndeclaredThrowableException(gse);
    35. }
    36. }
    37. /**
    38. * This method converts a HEX string to Byte[]
    39. *
    40. * @param hex: the HEX string
    41. *
    42. * @return: a byte array
    43. */
    44. private static byte[] hexStr2Bytes(String hex){
    45. // Adding one byte to get the right conversion
    46. // Values starting with "0" can be converted
    47. byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
    48. // Copy all the REAL bytes, not the "first"
    49. byte[] ret = new byte[bArray.length - 1];
    50. for (int i = 0; i < ret.length; i++)
    51. ret[i] = bArray[i+1];
    52. return ret;
    53. }
    54. private static final int[] DIGITS_POWER
    55. // 0 1 2 3 4 5 6 7 8
    56. = {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
    57. /**
    58. * This method generates a TOTP value for the given
    59. * set of parameters.
    60. *
    61. * @param key: the shared secret, HEX encoded
    62. * @param time: a value that reflects a time
    63. * @param returnDigits: number of digits to return
    64. *
    65. * @return: a numeric String in base 10 that includes
    66. * {@link truncationDigits} digits
    67. */
    68. public static String generateTOTP(String key,
    69. String time,
    70. String returnDigits){
    71. return generateTOTP(key, time, returnDigits, "HmacSHA1");
    72. }
    73. /**
    74. * 此方法为给定的一组参数生成一个 TOTP 值。
    75. *
    76. * @param key: the shared secret, HEX encoded
    77. * @param time: a value that reflects a time
    78. * @param returnDigits: number of digits to return
    79. *
    80. * @return: a numeric String in base 10 that includes
    81. * {@link truncationDigits} digits
    82. */
    83. public static String generateTOTP256(String key,
    84. String time,
    85. String returnDigits){
    86. return generateTOTP(key, time, returnDigits, "HmacSHA256");
    87. }
    88. /**
    89. * This method generates a TOTP value for the given
    90. * set of parameters.
    91. *
    92. * @param key: the shared secret, HEX encoded
    93. * @param time: a value that reflects a time
    94. * @param returnDigits: number of digits to return
    95. *
    96. * @return: a numeric String in base 10 that includes
    97. * {@link truncationDigits} digits
    98. */
    99. public static String generateTOTP512(String key,
    100. String time,
    101. String returnDigits){
    102. return generateTOTP(key, time, returnDigits, "HmacSHA512");
    103. }
    104. /**
    105. * This method generates a TOTP value for the given
    106. * set of parameters.
    107. *
    108. * @param key: the shared secret, HEX encoded
    109. * @param time: a value that reflects a time
    110. * @param returnDigits: number of digits to return
    111. * @param crypto: the crypto function to use
    112. *
    113. * @return: a numeric String in base 10 that includes
    114. * {@link truncationDigits} digits
    115. */
    116. public static String generateTOTP(String key,
    117. String time,
    118. String returnDigits,
    119. String crypto){
    120. int codeDigits = Integer.decode(returnDigits).intValue();
    121. String result = null;
    122. // Using the counter
    123. // First 8 bytes are for the movingFactor
    124. // Compliant with base RFC 4226 (HOTP)
    125. while (time.length() < 16 )
    126. time = "0" + time;
    127. // Get the HEX in a Byte[]
    128. byte[] msg = hexStr2Bytes(time);
    129. byte[] k = hexStr2Bytes(key);
    130. byte[] hash = hmac_sha(crypto, k, msg);
    131. // put selected bytes into result int
    132. int offset = hash[hash.length - 1] & 0xf;
    133. int binary =
    134. ((hash[offset] & 0x7f) << 24) |
    135. ((hash[offset + 1] & 0xff) << 16) |
    136. ((hash[offset + 2] & 0xff) << 8) |
    137. (hash[offset + 3] & 0xff);
    138. int otp = binary % DIGITS_POWER[codeDigits];
    139. result = Integer.toString(otp);
    140. while (result.length() < codeDigits) {
    141. result = "0" + result;
    142. }
    143. return result;
    144. }
    145. public static void main(String[] args) {
    146. // Seed for HMAC-SHA1 - 20 bytes
    147. String seed = "3132333435363738393031323334353637383930";
    148. // Seed for HMAC-SHA256 - 32 bytes
    149. String seed32 = "3132333435363738393031323334353637383930" +
    150. "313233343536373839303132";
    151. // Seed for HMAC-SHA512 - 64 bytes
    152. String seed64 = "3132333435363738393031323334353637383930" +
    153. "3132333435363738393031323334353637383930" +
    154. "3132333435363738393031323334353637383930" +
    155. "31323334";
    156. long T0 = 0;
    157. long X = 30;
    158. long testTime[] = {59L, 1111111109L, 1111111111L,
    159. 1234567890L, 2000000000L, 20000000000L};
    160. String steps = "0";
    161. DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    162. df.setTimeZone(TimeZone.getTimeZone("UTC"));
    163. try {
    164. System.out.println(
    165. "+---------------+-----------------------+" +
    166. "------------------+--------+--------+");
    167. System.out.println(
    168. "| Time(sec) | Time (UTC format) " +
    169. "| Value of T(Hex) | TOTP | Mode |");
    170. System.out.println(
    171. "+---------------+-----------------------+" +
    172. "------------------+--------+--------+");
    173. for (int i=0; i<testTime.length; i++) {
    174. long T = (testTime[i] - T0)/X;
    175. steps = Long.toHexString(T).toUpperCase();
    176. while (steps.length() < 16) steps = "0" + steps;
    177. String fmtTime = String.format("%1$-11s", testTime[i]);
    178. String utcTime = df.format(new Date(testTime[i]*1000));
    179. System.out.print("| " + fmtTime + " | " + utcTime +
    180. " | " + steps + " |");
    181. System.out.println(generateTOTP(seed, steps, "8",
    182. "HmacSHA1") + "| SHA1 |");
    183. System.out.print("| " + fmtTime + " | " + utcTime +
    184. " | " + steps + " |");
    185. System.out.println(generateTOTP(seed32, steps, "8",
    186. "HmacSHA256") + "| SHA256 |");
    187. System.out.print("| " + fmtTime + " | " + utcTime +
    188. " | " + steps + " |");
    189. System.out.println(generateTOTP(seed64, steps, "8",
    190. "HmacSHA512") + "| SHA512 |");
    191. System.out.println(
    192. "+---------------+-----------------------+" +
    193. "------------------+--------+--------+");
    194. }
    195. }catch (final Exception e){
    196. System.out.println("Error : " + e);
    197. }
    198. }
    199. }

    3、 go实现参考

    GitHub - tilaklodha/google-authenticatorContribute to tilaklodha/google-authenticator development by creating an account on GitHub.https://github.com/tilaklodha/google-authenticator

             下面的链接是评论区的小伙伴,说是正解,不过没时间看,这里记录一下,以供后面的人参考。

    https://github.com/350984267/TOTPicon-default.png?t=N5F7https://github.com/350984267/TOTP 

  • 相关阅读:
    Nagios自定义插件的编写规范
    SAGAN
    109.网络安全渗透测试—[权限提升篇7]—[Windows Mysql UDF提权]
    Discrete Optimization课程笔记(1)—Knapsack Problem
    python字面量
    【VUE疑难解决】:创建项目时卡在reify:ajv: timing reifyNode:node_modules/eslint
    Android 12 init(3) 属性服务
    ORA-09925 Unable to create audit trail file
    万字长文:FineBI面试题及参考答案详解
    C语言 二叉树
  • 原文地址:https://blog.csdn.net/bashendixie5/article/details/126578958