動態令牌-(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):
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的簡單應用
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(
316439
,
1401
)
# => True
hotp.verify(
316439
,
1402
)
# => False
|
使用場景
- 服務器登錄動態密碼驗證(如阿里雲ECS登錄,騰訊機房服務器登錄等);
- 公司VPN登錄雙因素驗證;
- 網絡接入radius動態密碼;
- 銀行轉賬動態密碼;
- 網銀、網絡游戲的實體動態口令牌;
- 等動態密碼驗證的應用場景。
市面上基於HOTP的產品
Google基於TOTP的開源實現
https://github.com/google/google-authenticator
golang的一個otp做的不錯的實現
https://github.com/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.
Done.