仰望星辰工作室

starmusic-pc

starmusic-pc/ frontend/src/common/crypto.js 19.6 KB · 587 行 原始文件
1/**
2 * 加密/编码工具库(纯 JS 实现,兼容 H5 与 App 双端)
3 * 用于构建自定义源脚本的 lx.utils 对象
4 */
5
6// ============ base64 ============
7const B64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
8
9/** UTF-8 编码字符串为字节数组 */
10export const utf8ToBytes = (str) => {
11 const bytes = []
12 for (let i = 0; i < str.length; i++) {
13 const code = str.charCodeAt(i)
14 if (code < 0x80) {
15 bytes.push(code)
16 } else if (code < 0x800) {
17 bytes.push((code >> 6) | 0xc0, (code & 0x3f) | 0x80)
18 } else if (code >= 0xd800 && code <= 0xdbff && i + 1 < str.length) {
19 const next = str.charCodeAt(i + 1)
20 const combined = ((code - 0xd800) << 10) + (next - 0xdc00) + 0x10000
21 bytes.push(
22 (combined >> 18) | 0xf0,
23 ((combined >> 12) & 0x3f) | 0x80,
24 ((combined >> 6) & 0x3f) | 0x80,
25 (combined & 0x3f) | 0x80,
26 )
27 i++
28 } else {
29 bytes.push((code >> 12) | 0xe0, ((code >> 6) & 0x3f) | 0x80, (code & 0x3f) | 0x80)
30 }
31 }
32 return bytes
33}
34
35/** 字节数组解码为 UTF-8 字符串 */
36export const bytesToUtf8 = (bytes) => {
37 let result = ''
38 let i = 0
39 const arr = Array.from(bytes)
40 while (i < arr.length) {
41 const byte = arr[i]
42 if (byte < 0x80) {
43 result += String.fromCharCode(byte)
44 i++
45 } else if (byte >= 0xc0 && byte < 0xe0) {
46 result += String.fromCharCode(((byte & 0x1f) << 6) | (arr[i + 1] & 0x3f))
47 i += 2
48 } else if (byte >= 0xe0 && byte < 0xf0) {
49 result += String.fromCharCode(((byte & 0x0f) << 12) | ((arr[i + 1] & 0x3f) << 6) | (arr[i + 2] & 0x3f))
50 i += 3
51 } else {
52 const code = ((byte & 0x07) << 18) | ((arr[i + 1] & 0x3f) << 12) | ((arr[i + 2] & 0x3f) << 6) | (arr[i + 3] & 0x3f)
53 result += String.fromCharCode(((code - 0x10000) >> 10) + 0xd800, ((code - 0x10000) & 0x3ff) + 0xdc00)
54 i += 4
55 }
56 }
57 return result
58}
59
60/** 字节数组 -> base64 字符串 */
61export const bytesToB64 = (bytes) => {
62 const arr = Array.from(bytes)
63 let result = ''
64 for (let i = 0; i < arr.length; i += 3) {
65 const b1 = arr[i]
66 const b2 = i + 1 < arr.length ? arr[i + 1] : null
67 const b3 = i + 2 < arr.length ? arr[i + 2] : null
68 result += B64_CHARS[b1 >> 2]
69 result += B64_CHARS[((b1 & 0x3) << 4) | (b2 != null ? b2 >> 4 : 0)]
70 result += b2 != null ? B64_CHARS[((b2 & 0xf) << 2) | (b3 != null ? b3 >> 6 : 0)] : '='
71 result += b3 != null ? B64_CHARS[b3 & 0x3f] : '='
72 }
73 return result
74}
75
76/** base64 字符串 -> 字节数组 */
77export const b64ToBytes = (b64) => {
78 const str = String(b64).replace(/[^A-Za-z0-9+/=]/g, '')
79 const result = []
80 for (let i = 0; i < str.length; i += 4) {
81 const c1 = B64_CHARS.indexOf(str[i])
82 const c2 = B64_CHARS.indexOf(str[i + 1])
83 const c3 = str[i + 2] === '=' ? null : B64_CHARS.indexOf(str[i + 2])
84 const c4 = str[i + 3] === '=' ? null : B64_CHARS.indexOf(str[i + 3])
85 result.push((c1 << 2) | (c2 >> 4))
86 if (c3 != null) result.push(((c2 & 0xf) << 4) | (c3 >> 2))
87 if (c4 != null) result.push(((c3 & 0x3) << 6) | c4)
88 }
89 return result
90}
91
92/** 字符串 -> base64(UTF-8 编码) */
93export const strToB64 = (str) => bytesToB64(utf8ToBytes(str))
94
95/** base64 -> 字符串(UTF-8 解码) */
96export const b64ToStr = (b64) => bytesToUtf8(b64ToBytes(b64))
97
98// ============ hex ============
99export const bytesToHex = (bytes) => Array.from(bytes).reduce((s, b) => s + b.toString(16).padStart(2, '0'), '')
100export const hexToBytes = (hex) => {
101 const result = []
102 for (let i = 0; i < hex.length; i += 2) result.push(parseInt(hex.substr(i, 2), 16))
103 return result
104}
105
106// ============ MD5 ============
107// 经典 MD5 实现(Paul Johnston 版,已验证)
108export const md5 = (input) => {
109 const str = String(input)
110
111 const core_md5 = (x, len) => {
112 x[len >> 5] |= 0x80 << (len % 32)
113 x[(((len + 64) >>> 9) << 4) + 14] = len
114
115 let a = 1732584193
116 let b = -271733879
117 let c = -1732584194
118 let d = 271733878
119
120 for (let i = 0; i < x.length; i += 16) {
121 const olda = a
122 const oldb = b
123 const oldc = c
124 const oldd = d
125
126 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936)
127 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586)
128 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819)
129 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330)
130 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897)
131 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426)
132 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341)
133 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983)
134 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416)
135 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417)
136 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063)
137 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162)
138 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682)
139 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101)
140 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290)
141 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329)
142
143 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510)
144 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632)
145 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713)
146 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302)
147 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691)
148 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083)
149 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335)
150 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848)
151 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438)
152 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690)
153 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961)
154 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501)
155 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467)
156 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784)
157 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473)
158 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734)
159
160 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558)
161 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463)
162 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562)
163 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556)
164 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060)
165 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353)
166 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632)
167 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640)
168 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174)
169 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222)
170 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979)
171 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189)
172 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487)
173 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835)
174 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520)
175 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651)
176
177 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844)
178 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415)
179 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905)
180 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055)
181 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571)
182 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606)
183 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523)
184 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799)
185 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359)
186 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744)
187 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380)
188 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649)
189 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070)
190 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379)
191 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259)
192 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551)
193
194 a = safe_add(a, olda)
195 b = safe_add(b, oldb)
196 c = safe_add(c, oldc)
197 d = safe_add(d, oldd)
198 }
199 return Array(a, b, c, d)
200 }
201
202 const md5_cmn = (q, a, b, x, s, t) => safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
203 const md5_ff = (a, b, c, d, x, s, t) => md5_cmn((b & c) | (~b & d), a, b, x, s, t)
204 const md5_gg = (a, b, c, d, x, s, t) => md5_cmn((b & d) | (c & ~d), a, b, x, s, t)
205 const md5_hh = (a, b, c, d, x, s, t) => md5_cmn(b ^ c ^ d, a, b, x, s, t)
206 const md5_ii = (a, b, c, d, x, s, t) => md5_cmn(c ^ (b | ~d), a, b, x, s, t)
207
208 const safe_add = (x, y) => {
209 const lsw = (x & 0xffff) + (y & 0xffff)
210 const msw = (x >> 16) + (y >> 16) + (lsw >> 16)
211 return (msw << 16) | (lsw & 0xffff)
212 }
213
214 const bit_rol = (num, cnt) => (num << cnt) | (num >>> (32 - cnt))
215
216 const str2binl = (str) => {
217 const bin = []
218 const mask = (1 << 8) - 1
219 for (let i = 0; i < str.length * 8; i += 8) {
220 bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << (i % 32)
221 }
222 return bin
223 }
224
225 const binl2hex = (binarray) => {
226 const hex_tab = '0123456789abcdef'
227 let str = ''
228 for (let i = 0; i < binarray.length * 4; i++) {
229 str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xf) + hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xf)
230 }
231 return str
232 }
233
234 const binarray = str2binl(str)
235 return binl2hex(core_md5(binarray, str.length * 8))
236}
237
238// ============ AES-128 (ECB/CBC, PKCS7) ============
239const AES_SBOX = [
240 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
241 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
242 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
243 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
244 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
245 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
246 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
247 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
248 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
249 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
250 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
251 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
252 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
253 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
254 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
255 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
256]
257
258const AES_RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
259
260const aesGmul = (a, b) => {
261 let p = 0
262 let counter = 0
263 let hiBitSet
264 while (counter < 8) {
265 if (b & 1) p ^= a
266 hiBitSet = a & 0x80
267 a = (a << 1) & 0xff
268 if (hiBitSet) a ^= 0x1b
269 b >>= 1
270 counter++
271 }
272 return p
273}
274
275const aesKeyExpansion = (key) => {
276 const w = []
277 for (let i = 0; i < 4; i++) {
278 w.push([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]])
279 }
280 for (let i = 4; i < 44; i++) {
281 let temp = [...w[i - 1]]
282 if (i % 4 === 0) {
283 temp = [temp[1], temp[2], temp[3], temp[0]].map((b, idx) => AES_SBOX[b] ^ (idx === 0 ? AES_RCON[i / 4] : 0))
284 }
285 w.push([
286 w[i - 4][0] ^ temp[0],
287 w[i - 4][1] ^ temp[1],
288 w[i - 4][2] ^ temp[2],
289 w[i - 4][3] ^ temp[3],
290 ])
291 }
292 return w
293}
294
295const aesAddRoundKey = (state, w, round) => {
296 for (let c = 0; c < 4; c++) {
297 for (let r = 0; r < 4; r++) {
298 state[r][c] ^= w[round * 4 + c][r]
299 }
300 }
301}
302
303const aesSubBytes = (state) => {
304 for (let r = 0; r < 4; r++) {
305 for (let c = 0; c < 4; c++) {
306 state[r][c] = AES_SBOX[state[r][c]]
307 }
308 }
309}
310
311const aesShiftRows = (state) => {
312 for (let r = 1; r < 4; r++) {
313 const row = [state[r][0], state[r][1], state[r][2], state[r][3]]
314 for (let c = 0; c < 4; c++) {
315 state[r][c] = row[(c + r) % 4]
316 }
317 }
318}
319
320const aesMixColumns = (state) => {
321 for (let c = 0; c < 4; c++) {
322 const a = [state[0][c], state[1][c], state[2][c], state[3][c]]
323 state[0][c] = aesGmul(a[0], 2) ^ aesGmul(a[1], 3) ^ a[2] ^ a[3]
324 state[1][c] = a[0] ^ aesGmul(a[1], 2) ^ aesGmul(a[2], 3) ^ a[3]
325 state[2][c] = a[0] ^ a[1] ^ aesGmul(a[2], 2) ^ aesGmul(a[3], 3)
326 state[3][c] = aesGmul(a[0], 3) ^ a[1] ^ a[2] ^ aesGmul(a[3], 2)
327 }
328}
329
330/** AES-128 加密一块(16 字节) */
331const aesEncryptBlock = (block, w) => {
332 const state = [
333 [block[0], block[4], block[8], block[12]],
334 [block[1], block[5], block[9], block[13]],
335 [block[2], block[6], block[10], block[14]],
336 [block[3], block[7], block[11], block[15]],
337 ]
338 aesAddRoundKey(state, w, 0)
339 for (let round = 1; round < 10; round++) {
340 aesSubBytes(state)
341 aesShiftRows(state)
342 aesMixColumns(state)
343 aesAddRoundKey(state, w, round)
344 }
345 aesSubBytes(state)
346 aesShiftRows(state)
347 aesAddRoundKey(state, w, 10)
348 const out = []
349 for (let c = 0; c < 4; c++) {
350 for (let r = 0; r < 4; r++) out.push(state[r][c])
351 }
352 return out
353}
354
355/**
356 * AES-128 加密
357 * @param {Uint8Array|number[]} data 明文
358 * @param {string} mode aes-128-ecb / aes-128-cbc
359 * @param {Uint8Array|number[]} key 16 字节密钥
360 * @param {Uint8Array|number[]} iv 16 字节 IV(ECB 可忽略)
361 * @returns {Uint8Array}
362 */
363export const aesEncrypt = (data, mode, key, iv) => {
364 const k = Array.from(key).slice(0, 16)
365 const w = aesKeyExpansion(k)
366 const input = Array.from(data)
367 // PKCS7 填充
368 const padLen = 16 - (input.length % 16)
369 for (let i = 0; i < padLen; i++) input.push(padLen)
370 const result = []
371 let prev = iv ? Array.from(iv).slice(0, 16) : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
372 for (let i = 0; i < input.length; i += 16) {
373 let block = input.slice(i, i + 16)
374 if (mode === 'aes-128-cbc') {
375 block = block.map((b, idx) => b ^ prev[idx])
376 }
377 const encrypted = aesEncryptBlock(block, w)
378 if (mode === 'aes-128-cbc') prev = encrypted
379 result.push(...encrypted)
380 }
381 return new Uint8Array(result)
382}
383
384// ============ RSA(BigInt 实现,NoPadding 加密) ============
385/** 从 PEM/DER 公钥中提取 n, e */
386export const parsePublicKey = (key) => {
387 let base64 = String(key)
388 .replace(/-----BEGIN PUBLIC KEY-----/g, '')
389 .replace(/-----END PUBLIC KEY-----/g, '')
390 .replace(/-----BEGIN RSA PUBLIC KEY-----/g, '')
391 .replace(/-----END RSA PUBLIC KEY-----/g, '')
392 .replace(/\s+/g, '')
393 const der = b64ToBytes(base64)
394
395 const readLength = (bytes, pos) => {
396 const first = bytes[pos]
397 pos++
398 if (first < 0x80) return { length: first, pos }
399 const numBytes = first & 0x7f
400 let length = 0
401 for (let i = 0; i < numBytes; i++) {
402 length = (length << 8) | bytes[pos]
403 pos++
404 }
405 return { length, pos }
406 }
407
408 // 遍历 DER,找到 BIT STRING(0x03,X.509)或直接定位 INTEGER(0x02,PKCS#1)
409 // 兼容两种编码:X.509 SubjectPublicKeyInfo 与 PKCS#1 RSAPublicKey
410 let pos = 0
411 // 跳过最外层 SEQUENCE
412 if (der[pos] !== 0x30) throw new Error('Invalid public key')
413 pos++
414 pos = readLength(der, pos).pos
415
416 // 若下一元素是 SEQUENCE(AlgorithmIdentifier),则按 X.509 解析
417 if (der[pos] === 0x30) {
418 pos++
419 pos = readLength(der, pos).pos
420 // OID
421 if (der[pos] !== 0x06) throw new Error('Invalid public key')
422 pos++
423 const oidLen = readLength(der, pos)
424 pos = oidLen.pos + oidLen.length
425 // NULL 参数(可能有)
426 if (der[pos] === 0x05) {
427 pos++
428 pos = readLength(der, pos).pos
429 }
430 // BIT STRING
431 if (der[pos] !== 0x03) throw new Error('Invalid public key')
432 pos++
433 pos = readLength(der, pos).pos
434 pos++ // 跳过 unused bits 字节
435 // 内层 SEQUENCE(RSAPublicKey)
436 if (der[pos] !== 0x30) throw new Error('Invalid public key')
437 pos++
438 pos = readLength(der, pos).pos
439 }
440
441 // 此时定位到 INTEGER n
442 if (der[pos] !== 0x02) throw new Error('Invalid public key')
443 pos++
444 const nLen = readLength(der, pos)
445 const nStart = nLen.pos
446 const nEnd = nStart + nLen.length
447 pos = nEnd
448 // INTEGER e
449 if (der[pos] !== 0x02) throw new Error('Invalid public key')
450 pos++
451 const eLen = readLength(der, pos)
452 const eStart = eLen.pos
453 const eEnd = eStart + eLen.length
454 return { n: der.slice(nStart, nEnd), e: der.slice(eStart, eEnd) }
455}
456
457const bigIntFromBytes = (bytes) => {
458 let hex = bytesToHex(bytes)
459 // 去掉前导零(BigInt 能处理 0x 前缀)
460 return BigInt('0x' + (hex || '0'))
461}
462
463const bigIntToBytes = (bi, length) => {
464 let hex = bi.toString(16)
465 if (hex.length % 2) hex = '0' + hex
466 const bytes = hexToBytes(hex)
467 // 处理前导 00(用于正数)
468 if (bytes[0] === 0) bytes.shift()
469 // 填充到目标长度(模数长度)
470 const result = new Uint8Array(length)
471 const start = length - bytes.length
472 if (start >= 0) result.set(bytes, start)
473 return result
474}
475
476/**
477 * RSA 加密(NoPadding,BigInt 实现)
478 * @param {Uint8Array|number[]} data
479 * @param {string} key PEM 公钥
480 * @param {number} keyLength 模数位数(默认 1024)
481 * @returns {Uint8Array}
482 */
483export const rsaEncrypt = (data, key) => {
484 const { n, e } = parsePublicKey(key)
485 const nBig = bigIntFromBytes(n)
486 const eBig = bigIntFromBytes(e)
487 const keyByteLength = Math.ceil(nBig.toString(16).length / 2)
488 const m = bigIntFromBytes(Array.from(data))
489 if (m >= nBig) throw new Error('RSA data too long')
490 const c = modPow(m, eBig, nBig)
491 return bigIntToBytes(c, keyByteLength)
492}
493
494/** 快速模幂 (base^exp) mod mod */
495const modPow = (base, exp, mod) => {
496 let result = 1n
497 base = base % mod
498 while (exp > 0n) {
499 if (exp & 1n) result = (result * base) % mod
500 exp >>= 1n
501 base = (base * base) % mod
502 }
503 return result
504}
505
506// ============ SHA1 ============
507/** SHA-1(返回 hex 小写) */
508export const sha1 = (input) => {
509 const str = String(input)
510 const bytes = utf8ToBytes(str)
511 const bitLen = bytes.length * 8
512 // 填充
513 const padded = [...bytes, 0x80]
514 while (padded.length % 64 !== 56) padded.push(0)
515 // 64 位大端长度(不能用 >>>,JS 移位对 32 取模)
516 for (let i = 7; i >= 0; i--) {
517 padded.push(Math.floor(bitLen / Math.pow(2, i * 8)) % 256)
518 }
519
520 let h0 = 0x67452301
521 let h1 = 0xefcdab89
522 let h2 = 0x98badcfe
523 let h3 = 0x10325476
524 let h4 = 0xc3d2e1f0
525
526 const rotl = (num, cnt) => ((num << cnt) | (num >>> (32 - cnt))) >>> 0
527
528 for (let i = 0; i < padded.length; i += 64) {
529 const w = new Array(80)
530 for (let j = 0; j < 16; j++) {
531 const off = i + j * 4
532 w[j] = ((padded[off] << 24) | (padded[off + 1] << 16) | (padded[off + 2] << 8) | padded[off + 3]) >>> 0
533 }
534 for (let j = 16; j < 80; j++) {
535 w[j] = rotl(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1)
536 }
537
538 let a = h0
539 let b = h1
540 let c = h2
541 let d = h3
542 let e = h4
543
544 for (let j = 0; j < 80; j++) {
545 let f, k
546 if (j < 20) {
547 f = (b & c) | (~b & d)
548 k = 0x5a827999
549 } else if (j < 40) {
550 f = b ^ c ^ d
551 k = 0x6ed9eba1
552 } else if (j < 60) {
553 f = (b & c) | (b & d) | (c & d)
554 k = 0x8f1bbcdc
555 } else {
556 f = b ^ c ^ d
557 k = 0xca62c1d6
558 }
559 const temp = (rotl(a, 5) + f + e + k + w[j]) >>> 0
560 e = d
561 d = c
562 c = rotl(b, 30)
563 b = a
564 a = temp
565 }
566
567 h0 = (h0 + a) >>> 0
568 h1 = (h1 + b) >>> 0
569 h2 = (h2 + c) >>> 0
570 h3 = (h3 + d) >>> 0
571 h4 = (h4 + e) >>> 0
572 }
573
574 const toHex = (n) => n.toString(16).padStart(8, '0')
575 return toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4)
576}
577
578/** 随机字节 */
579export const randomBytes = (size) => {
580 const bytes = new Uint8Array(size)
581 if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
582 crypto.getRandomValues(bytes)
583 } else {
584 for (let i = 0; i < size; i++) bytes[i] = Math.floor(Math.random() * 256)
585 }
586 return bytes
587}