动态令牌-(OTP,HOTP,TOTP)-基本原理

名词解释和基本介绍

OTP 是 One-Time Password的简写,表示一次性密码。

HOTP 是HMAC-based One-Time Password的简写,表示基于HMAC算法加密的一次性密码。

  是事件同步,通过某一特定的事件次序及相同的种子值作为输入,通过HASH算法运算出一致的密码。

TOTP 是Time-based One-Time Password的简写,表示基于时间戳算法的一次性密码。 

  是时间同步,基于客户端的动态口令和动态口令验证服务器的时间比对,一般每60秒产生一个新口令,要求客户端和服务器能够十分精确的保持正确的时钟,客户端和服务端基于时间计算的动态口令才能一致。  

原理介绍

OTP基本原理

计算OTP串的公式

1

OTP(K,C) = Truncate(HMAC-SHA-1(K,C))

其中,

K表示秘钥串;

C是一个数字,表示随机数;

HMAC-SHA-1表示使用SHA-1做HMAC;

Truncate是一个函数,就是怎么截取加密后的串,并取加密后串的哪些字段组成一个数字。

对HMAC-SHA-1方式加密来说,Truncate实现如下。

  • HMAC-SHA-1加密后的长度得到一个20字节的密串;
  • 取这个20字节的密串的最后一个字节,取这字节的低4位,作为截取加密串的下标偏移量;
  • 按照下标偏移量开始,获取4个字节,按照大端方式组成一个整数;
  • 截取这个整数的后6位或者8位转成字符串返回。

 Java代码实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

public static String generateOTP(String K,

                                     String C,

                                     String returnDigits,

                                     String crypto){

       int codeDigits = Integer.decode(returnDigits).intValue();

       String result = null;

       // K是密码

       // C是产生的随机数

       // crypto是加密算法 HMAC-SHA-1

       byte[] hash = hmac_sha(crypto, K, C);

       // hash为20字节的字符串

       // put selected bytes into result int

       // 获取hash最后一个字节的低4位,作为选择结果的开始下标偏移

       int offset = hash[hash.length - 1] & 0xf;

       // 获取4个字节组成一个整数,其中第一个字节最高位为符号位,不获取,使用0x7f

       int binary =

               ((hash[offset] & 0x7f) << 24) |

               ((hash[offset + 1] & 0xff) << 16) |

               ((hash[offset + 2] & 0xff) << 8) |

               (hash[offset + 3] & 0xff);

       // 获取这个整数的后6位(可以根据需要取后8位)

       int otp = binary % 1000000;

       // 将数字转成字符串,不够6位前面补0

       result = Integer.toString(otp);

       while (result.length() < codeDigits) {

           result = "0" + result;

       }

       return result;

   }

返回的结果就是看到一个数字的动态密码。

HOTP基本原理

知道了OTP的基本原理,HOTP只是将其中的参数C变成了随机数

公式修改一下

1

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

HOTP: Generates the OTP for the given count

即:C作为一个参数,获取动态密码。

HOTP的python代码片段:

1

2

3

4

5

6

7

8

class HOTP(OTP):

    def at(self, count):

        """

        Generates the OTP for the given count

        @param [Integer] count counter

        @returns [Integer] OTP

        """

        return self.generate_otp(count)

一般规定HOTP的散列函数使用SHA2,即:基于SHA-256 or SHA-512 [SHA2] 的散列函数做事件同步验证;

TOTP基本原理

TOTP只是将其中的参数C变成了由时间戳产生的数字。

1

TOTP(K,C) = HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

不同点是TOTP中的C是时间戳计算得出。

1

C = (T - T0) / X;

T 表示当前Unix时间戳

T0一般取值为 0.

X 表示时间步数,也就是说多长时间产生一个动态密码,这个时间间隔就是时间步数X,系统默认是30秒;

例如:

T0 = 0;

X = 30;

T = 30 ~ 59, C = 1; 表示30 ~ 59 这30秒内的动态密码一致。

T = 60 ~ 89, C = 2; 表示30 ~ 59 这30秒内的动态密码一致。

不同厂家使用的时间步数不同;

  • 阿里巴巴的身份宝使用的时间步数是60秒;
  • 宁盾令牌使用的时间步数是60秒;
  • Google的 身份验证器的时间步数是30秒;
  • 腾讯的Token时间步数是60秒;

TOTP的python代码片段:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

class TOTP(OTP):

    def __init__(self*args, **kwargs):

        """

        @option options [Integer] interval (30) the time interval in seconds

            for OTP This defaults to 30 which is standard.

        """

        self.interval = kwargs.pop('interval'30)

        super(TOTP, self).__init__(*args, **kwargs)

    def now(self):

        """

        Generate the current time OTP

        @return [Integer] the OTP as an integer

        """

        return self.generate_otp(self.timecode(datetime.datetime.now()))

    def timecode(self, for_time):

        = time.mktime(for_time.timetuple())

        return int(i / self.interval)

代码说明

self.interval 是时间步数X

datetime.datetime.now()为当前的Unix时间戳

timecode表示(T - T0) / X,即获取获取动态密码计算的随机数。

TOTP 的实现可以使用HMAC-SHA-256或者HMAC-SHA-512散列函数;

python的otp实现

pyotp · PyPI

GitHub - pyauth/pyotp: Python One-Time Password Library

基于pyotp的简单应用

1

2

3

4

5

6

7

8

>>> import base64

>>> base64.b32encode('This is my secret key')

'KRUGS4ZANFZSA3LZEBZWKY3SMV2CA23FPE======'

>>> secretKey = base64.b32encode('This is my secret key')

>>> import pyotp

>>> totp = pyotp.TOTP(secretKey)

>>> totp.now()

423779

程序的简单说明

加载base64的模块,将我的秘钥做一下base32的加密,加载pyotp模块,otp使用base32加密后的秘钥传作为种子,生成随机数字验证的。

可以使用pyotp和expect一起实现基于google authenticator的自动登录(免去每次双认证,输入密码和动态密码)。

pyotp的TOTP的使用说明(官网)

1

2

3

4

5

6

7

totp = pyotp.TOTP('base32secret3232')

totp.now() # => 492039

# OTP verified for current time

totp.verify(492039# => True

time.sleep(30)

totp.verify(492039# => False

pyotp的HOTP的使用说明(官网) 

1

2

3

4

5

6

7

8

hotp = pyotp.HOTP('base32secret3232')

hotp.at(0# => 260182

hotp.at(1# => 55283

hotp.at(1401# => 316439

# OTP verified with a counter

hotp.verify(3164391401# => True

hotp.verify(3164391402# => False

使用场景

  • 服务器登录动态密码验证(如阿里云ECS登录,腾讯机房服务器登录等);
  • 公司VPN登录双因素验证;
  • 网络接入radius动态密码;
  • 银行转账动态密码;
  • 网银、网络游戏的实体动态口令牌;
  • 等动态密码验证的应用场景。

市面上基于HOTP的产品

Google基于TOTP的开源实现

GitHub - google/google-authenticator: Open source version of Google Authenticator (except the Android app)

RFC6238中TOTP基于java代码的实现。

golang的一个otp做的不错的实现

GitHub - gitchs/gootp

RFC参考

RFC 4226 One-Time Password and HMAC-based One-Time Password.

RFC 6238 Time-based One-Time Password.

RFC 2104 HMAC Keyed-Hashing for Message Authentication.

https://www.cnblogs.com/voipman/p/6216328.html原帖链接

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用 Java 生成动态令牌 OTP 的示例代码: ```java import java.util.Date; import java.util.Random; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base32; public class OTPUtil { private static final int DIGITS = 6; private static final int PERIOD = 30; public static String generateOTP(String secretKey) { try { byte[] keyBytes = new Base32().decode(secretKey); // 获取当前时间戳,单位为秒 long timestamp = new Date().getTime() / 1000; // 计算时间戳的时间段 long counter = timestamp / PERIOD; // 将计数器转为字节数组 byte[] counterBytes = new byte[8]; for (int i = counterBytes.length - 1; i >= 0; i--) { counterBytes[i] = (byte) (counter & 0xff); counter >>= 8; } // 计算 HMAC-SHA1 哈希值 Mac mac = Mac.getInstance("HmacSHA1"); mac.init(new SecretKeySpec(keyBytes, "RAW")); byte[] hash = mac.doFinal(counterBytes); // 获取哈希值的低位 4 位字节 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); // 对低位 4 位字节进行模运算 int otp = binary % (int) Math.pow(10, DIGITS); // 将 OTP 转为字符串 String otpStr = Integer.toString(otp); while (otpStr.length() < DIGITS) { otpStr = "0" + otpStr; } return otpStr; } catch (Exception e) { throw new RuntimeException(e); } } public static String generateSecretKey() { byte[] bytes = new byte[20]; new Random().nextBytes(bytes); return new Base32().encodeAsString(bytes); } } ``` 这段代码使用了 Google Authenticator 的算法,可以生成 6 位数字的动态令牌 OTP。其中 `generateSecretKey` 方法用于生成随机的密钥,可以将生成的密钥通过二维码等方式传递给用户,让用户将其添加到 Authenticator 应用中。`generateOTP` 方法用于根据密钥生成动态令牌 OTP,可以在用户登录、重置密码等需要验证身份的场景中使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值