Skip to content

Commit bf205d5

Browse files
[投稿] 添加脚本: 验证器的密钥生成器 — by dreamingbob
1 parent ecd92ca commit bf205d5

1 file changed

Lines changed: 159 additions & 0 deletions

File tree

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import base64
2+
import secrets
3+
import hmac
4+
import hashlib
5+
import struct
6+
import time
7+
import urllib.parse
8+
9+
10+
def generate_base32_secret(byte_length=20):
11+
random_bytes = secrets.token_bytes(byte_length)
12+
secret = base64.b32encode(random_bytes).decode("utf-8")
13+
return secret.rstrip("=")
14+
15+
16+
def build_otpauth_uri(secret, account_name, issuer_name):
17+
label = issuer_name + ":" + account_name
18+
19+
params = {
20+
"secret": secret,
21+
"issuer": issuer_name,
22+
"algorithm": "SHA1",
23+
"digits": "6",
24+
"period": "30"
25+
}
26+
27+
uri = "otpauth://totp/" + urllib.parse.quote(label)
28+
uri += "?" + urllib.parse.urlencode(params)
29+
return uri
30+
31+
32+
def base32_decode_no_padding(secret):
33+
secret = secret.strip().replace(" ", "").upper()
34+
missing_padding = len(secret) % 8
35+
36+
if missing_padding:
37+
secret += "=" * (8 - missing_padding)
38+
39+
return base64.b32decode(secret)
40+
41+
42+
def generate_totp(secret, interval=30, digits=6):
43+
key = base32_decode_no_padding(secret)
44+
counter = int(time.time() // interval)
45+
counter_bytes = struct.pack(">Q", counter)
46+
47+
hmac_hash = hmac.new(
48+
key,
49+
counter_bytes,
50+
hashlib.sha1
51+
).digest()
52+
53+
offset = hmac_hash[-1] & 0x0F
54+
55+
code_int = struct.unpack(">I", hmac_hash[offset:offset + 4])[0]
56+
code_int = code_int & 0x7FFFFFFF
57+
58+
code = code_int % (10 ** digits)
59+
return str(code).zfill(digits)
60+
61+
62+
def verify_totp(secret, user_code, interval=30, digits=6, window=1):
63+
current_time = int(time.time())
64+
key = base32_decode_no_padding(secret)
65+
66+
for offset in range(-window, window + 1):
67+
test_time = current_time + offset * interval
68+
counter = int(test_time // interval)
69+
counter_bytes = struct.pack(">Q", counter)
70+
71+
hmac_hash = hmac.new(
72+
key,
73+
counter_bytes,
74+
hashlib.sha1
75+
).digest()
76+
77+
dynamic_offset = hmac_hash[-1] & 0x0F
78+
79+
code_int = struct.unpack(">I", hmac_hash[dynamic_offset:dynamic_offset + 4])[0]
80+
code_int = code_int & 0x7FFFFFFF
81+
82+
expected_code = str(code_int % (10 ** digits)).zfill(digits)
83+
84+
if expected_code == user_code:
85+
return True
86+
87+
return False
88+
89+
90+
def main():
91+
print("======================================")
92+
print(" Google Authenticator 密钥生成器")
93+
print(" 手机 Python IDE 纯 Python 版")
94+
print("======================================")
95+
96+
issuer_name = input("请输入服务名称,例如 MyApp:").strip()
97+
account_name = input("请输入账号名称,例如 ding@example.com:").strip()
98+
99+
if issuer_name == "":
100+
issuer_name = "MyApp"
101+
102+
if account_name == "":
103+
account_name = "user@example.com"
104+
105+
secret = generate_base32_secret()
106+
uri = build_otpauth_uri(secret, account_name, issuer_name)
107+
108+
print("")
109+
print("生成完成:")
110+
print("--------------------------------------")
111+
print("服务名称:", issuer_name)
112+
print("账号名称:", account_name)
113+
print("TOTP 密钥:")
114+
print(secret)
115+
print("--------------------------------------")
116+
print("Google Authenticator 链接:")
117+
print(uri)
118+
print("--------------------------------------")
119+
120+
print("")
121+
print("使用方法:")
122+
print("1. 打开 Google Authenticator")
123+
print("2. 选择添加代码")
124+
print("3. 选择输入设置密钥")
125+
print("4. 账号名称填写上面的账号名称")
126+
print("5. 密钥填写上面的 TOTP 密钥")
127+
print("6. 密钥类型选择基于时间")
128+
129+
current_code = generate_totp(secret)
130+
print("")
131+
print("当前这台设备根据密钥算出的 6 位验证码是:")
132+
print(current_code)
133+
print("这个数字应该和 Google Authenticator 里显示的数字一致。")
134+
135+
while True:
136+
print("")
137+
user_code = input("输入 Google Authenticator 当前显示的 6 位验证码测试,直接回车退出:").strip()
138+
139+
if user_code == "":
140+
print("已退出。请妥善保存密钥。")
141+
break
142+
143+
if not user_code.isdigit() or len(user_code) != 6:
144+
print("请输入 6 位数字。")
145+
continue
146+
147+
if verify_totp(secret, user_code):
148+
print("验证成功,这个密钥可以正常使用。")
149+
break
150+
else:
151+
print("验证失败。请检查手机时间是否准确,或等待下一个 30 秒验证码。")
152+
153+
print("")
154+
print("重要提醒:")
155+
print("TOTP 密钥不要发给别人。")
156+
print("谁拿到密钥,谁就能生成同样的验证码。")
157+
158+
159+
main()

0 commit comments

Comments
 (0)