关于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 算法。

扫描二维码关注公众号,回复: 14507946 查看本文章

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

RFC 6238: TOTP: Time-Based One-Time Password Algorithmicon-default.png?t=M7J4https://www.rfc-editor.org/rfc/rfc6238

2、Java参考代码

 import java.lang.reflect.UndeclaredThrowableException;
 import java.security.GeneralSecurityException;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 import java.math.BigInteger;
 import java.util.TimeZone;


 /**
  * 这是 OATH TOTP 算法的示例实现。
  * Visit www.openauthentication.org for more information.
  */

 public class TOTP {

     private TOTP() {}

     /**
      * 此方法使用 JCE 提供加密算法。 HMAC 使用加密散列算法作为参数计算散列消息验证码。
      *
      * @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256,
      *                             HmacSHA512)
      * @param keyBytes: the bytes to use for the HMAC key
      * @param text: the message or text to be authenticated
      */
     private static byte[] hmac_sha(String crypto, byte[] keyBytes,
             byte[] text){
         try {
             Mac hmac;
             hmac = Mac.getInstance(crypto);
             SecretKeySpec macKey =
                 new SecretKeySpec(keyBytes, "RAW");
             hmac.init(macKey);
             return hmac.doFinal(text);
         } catch (GeneralSecurityException gse) {
             throw new UndeclaredThrowableException(gse);
         }
     }


     /**
      * This method converts a HEX string to Byte[]
      *
      * @param hex: the HEX string
      *
      * @return: a byte array
      */
     private static byte[] hexStr2Bytes(String hex){
         // Adding one byte to get the right conversion
         // Values starting with "0" can be converted
         byte[] bArray = new BigInteger("10" + hex,16).toByteArray();

         // Copy all the REAL bytes, not the "first"
         byte[] ret = new byte[bArray.length - 1];
         for (int i = 0; i < ret.length; i++)
             ret[i] = bArray[i+1];
         return ret;
     }

     private static final int[] DIGITS_POWER
     // 0 1  2   3    4     5      6       7        8
     = {1,10,100,1000,10000,100000,1000000,10000000,100000000 };

     /**
      * This method generates a TOTP value for the given
      * set of parameters.
      *
      * @param key: the shared secret, HEX encoded
      * @param time: a value that reflects a time
      * @param returnDigits: number of digits to return
      *
      * @return: a numeric String in base 10 that includes
      *              {@link truncationDigits} digits
      */

     public static String generateTOTP(String key,
             String time,
             String returnDigits){
         return generateTOTP(key, time, returnDigits, "HmacSHA1");
     }


     /**
      * 此方法为给定的一组参数生成一个 TOTP 值。
      *
      * @param key: the shared secret, HEX encoded
      * @param time: a value that reflects a time
      * @param returnDigits: number of digits to return
      *
      * @return: a numeric String in base 10 that includes
      *              {@link truncationDigits} digits
      */

     public static String generateTOTP256(String key,
             String time,
             String returnDigits){
         return generateTOTP(key, time, returnDigits, "HmacSHA256");
     }

     /**
      * This method generates a TOTP value for the given
      * set of parameters.
      *
      * @param key: the shared secret, HEX encoded
      * @param time: a value that reflects a time
      * @param returnDigits: number of digits to return
      *
      * @return: a numeric String in base 10 that includes
      *              {@link truncationDigits} digits
      */

     public static String generateTOTP512(String key,
             String time,
             String returnDigits){
         return generateTOTP(key, time, returnDigits, "HmacSHA512");
     }


     /**
      * This method generates a TOTP value for the given
      * set of parameters.
      *
      * @param key: the shared secret, HEX encoded
      * @param time: a value that reflects a time
      * @param returnDigits: number of digits to return
      * @param crypto: the crypto function to use
      *
      * @return: a numeric String in base 10 that includes
      *              {@link truncationDigits} digits
      */

     public static String generateTOTP(String key,
             String time,
             String returnDigits,
             String crypto){
         int codeDigits = Integer.decode(returnDigits).intValue();
         String result = null;

         // Using the counter
         // First 8 bytes are for the movingFactor
         // Compliant with base RFC 4226 (HOTP)
         while (time.length() < 16 )
             time = "0" + time;

         // Get the HEX in a Byte[]
         byte[] msg = hexStr2Bytes(time);
         byte[] k = hexStr2Bytes(key);
         byte[] hash = hmac_sha(crypto, k, msg);

         // put selected bytes into result int
         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);

         int otp = binary % DIGITS_POWER[codeDigits];

         result = Integer.toString(otp);
         while (result.length() < codeDigits) {
             result = "0" + result;
         }
         return result;
     }

     public static void main(String[] args) {
         // Seed for HMAC-SHA1 - 20 bytes
         String seed = "3132333435363738393031323334353637383930";
         // Seed for HMAC-SHA256 - 32 bytes
         String seed32 = "3132333435363738393031323334353637383930" +
         "313233343536373839303132";
         // Seed for HMAC-SHA512 - 64 bytes
         String seed64 = "3132333435363738393031323334353637383930" +
         "3132333435363738393031323334353637383930" +
         "3132333435363738393031323334353637383930" +
         "31323334";
         long T0 = 0;
         long X = 30;
         long testTime[] = {59L, 1111111109L, 1111111111L,
                 1234567890L, 2000000000L, 20000000000L};

         String steps = "0";
         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         df.setTimeZone(TimeZone.getTimeZone("UTC"));

         try {
             System.out.println(
                     "+---------------+-----------------------+" +
             "------------------+--------+--------+");
             System.out.println(
                     "|  Time(sec)    |   Time (UTC format)   " +
             "| Value of T(Hex)  |  TOTP  | Mode   |");
             System.out.println(
                     "+---------------+-----------------------+" +
             "------------------+--------+--------+");

             for (int i=0; i<testTime.length; i++) {
                 long T = (testTime[i] - T0)/X;
                 steps = Long.toHexString(T).toUpperCase();
                 while (steps.length() < 16) steps = "0" + steps;
                 String fmtTime = String.format("%1$-11s", testTime[i]);
                 String utcTime = df.format(new Date(testTime[i]*1000));
                 System.out.print("|  " + fmtTime + "  |  " + utcTime +
                         "  | " + steps + " |");
                 System.out.println(generateTOTP(seed, steps, "8",
                 "HmacSHA1") + "| SHA1   |");
                 System.out.print("|  " + fmtTime + "  |  " + utcTime +
                         "  | " + steps + " |");
                 System.out.println(generateTOTP(seed32, steps, "8",
                 "HmacSHA256") + "| SHA256 |");
                 System.out.print("|  " + fmtTime + "  |  " + utcTime +
                         "  | " + steps + " |");
                 System.out.println(generateTOTP(seed64, steps, "8",
                 "HmacSHA512") + "| SHA512 |");

                 System.out.println(
                         "+---------------+-----------------------+" +
                 "------------------+--------+--------+");
             }
         }catch (final Exception e){
             System.out.println("Error : " + e);
         }
     }
 }

3、 go实现参考

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

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/126578958