-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
144 lines (127 loc) · 4.26 KB
/
index.html
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TOTP生成器</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background-color: #f5f5f5;
}
input {
padding: 10px;
margin: 5px 0;
width: 250px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 10px 15px;
border: none;
border-radius: 4px;
background-color: #28a745;
color: white;
cursor: pointer;
margin: 10px 0;
}
button:hover {
background-color: #218838;
}
#otpDisplay {
margin-top: 15px;
font-size: 24px;
font-weight: bold;
}
</style>
</head>
<body>
<h1>TOTP生成器</h1>
<input type="text" id="secretKey" placeholder="请在此输入密钥" />
<button id="generateOtp">生成验证码</button>
<button id="generateKey">不知道怎么用?生成一个密钥试试</button>
<div id="otpDisplay"></div>
<script type="d8070db513d19d52935bb093-text/javascript">
document.addEventListener('DOMContentLoaded', () => {
const secretKeyInput = document.getElementById('secretKey');
const queryParams = new URLSearchParams(window.location.search);
const secret = queryParams.get('secret');
if (secret) {
secretKeyInput.value = secret;
}
secretKeyInput.addEventListener('input', () => {
const otpDisplay = document.getElementById('otpDisplay');
otpDisplay.textContent = '';
});
document.getElementById('generateOtp').addEventListener('click', async () => {
const secret = secretKeyInput.value.trim();
if (secret) {
const otp = await generateTOTP(secret);
document.getElementById('otpDisplay').textContent = "验证码: " + otp;
} else {
document.getElementById('otpDisplay').textContent = "请输入有效的密钥。";
}
});
document.getElementById('generateKey').addEventListener('click', () => {
const randomKey = generateRandomBase32Key(16);
secretKeyInput.value = randomKey;
});
});
function base32ToUint8Array(base32) {
const base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let bits = '';
const bytes = [];
for (let char of base32) {
const value = base32Chars.indexOf(char.toUpperCase());
if (value === -1) continue;
bits += value.toString(2).padStart(5, '0');
}
for (let i = 0; i < bits.length; i += 8) {
const byte = bits.slice(i, i + 8);
if (byte.length === 8) {
bytes.push(parseInt(byte, 2));
}
}
return new Uint8Array(bytes);
}
function generateRandomBase32Key(length) {
const base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let key = '';
for (let i = 0; i < length; i++) {
key += base32Chars.charAt(Math.floor(Math.random() * base32Chars.length));
}
return key;
}
async function generateTOTP(secret) {
const timeStep = 30;
const epoch = Math.floor(Date.now() / 1000);
const timeCounter = Math.floor(epoch / timeStep);
const key = base32ToUint8Array(secret);
const timeBuffer = new Uint8Array(8);
let tempCounter = timeCounter;
for (let i = 7; i >= 0; i--) {
timeBuffer[i] = tempCounter & 0xff;
tempCounter = Math.floor(tempCounter / 256);
}
const hmac = await generateHMAC(key, timeBuffer);
const offset = (hmac[hmac.length - 1] & 0x0f);
const binCode = (hmac[offset] & 0x7f) << 24 |
(hmac[offset + 1] & 0xff) << 16 |
(hmac[offset + 2] & 0xff) << 8 |
(hmac[offset + 3] & 0xff);
const otp = binCode % 1000000;
return otp.toString().padStart(6, '0');
}
async function generateHMAC(key, timeBuffer) {
const keyParams = { name: "HMAC", hash: "SHA-1" };
const cryptoKey = await window.crypto.subtle.importKey("raw", key, keyParams, false, ["sign"]);
const signature = await window.crypto.subtle.sign("HMAC", cryptoKey, timeBuffer);
return new Uint8Array(signature);
}
</script>
</body>
</html>