OTP 是 One-Time Password的简写,表示一次性密码。
HOTP 是HMAC-based One-Time Password的简写,表示基于HMAC算法加密的一次性密码。
TOTP 是Time-based One-Time Password的简写,表示基于时间戳算法的一次性密码。
计算OTP串的公式
OTP(K,C) = Truncate(HMAC-SHA-1(K,C))
其中,
K表示秘钥串;
C是一个数字,表示随机数;
HMAC-SHA-1表示使用SHA-1做HMAC;
Truncate是一个函数,就是怎么截取加密后的串,并取加密后串的哪些字段组成一个数字。
对HMAC-SHA-1方式加密来说,Truncate实现如下。
Java代码实现
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;
}
返回的结果就是看到一个数字的动态密码。
知道了OTP的基本原理,HOTP只是将其中的参数C变成了随机数
公式修改一下
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
HOTP: Generates the OTP for the given count
即:C作为一个参数,获取动态密码。
HOTP的python代码片段:
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只是将其中的参数C变成了由时间戳产生的数字。
TOTP(K,C) = HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
不同点是TOTP中的C是时间戳计算得出。
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秒内的动态密码一致。
不同厂家使用的时间步数不同;
TOTP的python代码片段:
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):
i = 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实现
https://pypi.python.org/pypi/pyotp
https://github.com/pyotp/pyotp
基于pyotp的简单应用
>>> 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的使用说明(官网)
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的使用说明(官网)
hotp = pyotp.HOTP(‘base32secret3232‘)
hotp.at(0) # => 260182
hotp.at(1) # => 55283
hotp.at(1401) # => 316439
# OTP verified with a counter
hotp.verify(316439, 1401) # => True
hotp.verify(316439, 1402) # => False
https://github.com/google/google-authenticator
RFC6238中TOTP基于java代码的实现。
golang的一个otp做的不错的实现
https://github.com/gitchs/gootp
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.
Done.