function arrayBufferToBase64(buffer: ArrayBuffer): string { let binary = ''; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } async function getKey(password: string): Promise { const encoder = new TextEncoder() const data = encoder.encode(password) const hash = await crypto.subtle.digest('SHA-256', data) return hash } function readUint32BE(buffer: ArrayBuffer, offset: number = 0): number { const view = new DataView(buffer); return view.getUint32(offset, false); // false 表示使用大端(Big Endian) } /** * Web端 AES-256-GCM 解密 * @param encrypted - 加密后的数据(不包含认证标签) * @param keyBuffer - 原始密钥(32 字节) * @param ivBuffer - 初始化向量(12 字节) * @param authTag - 认证标签(16 字节) * @returns 解密后的明文字符串 */ async function decryptAES_GCM( encrypted: ArrayBuffer, keyBuffer: ArrayBuffer, ivBuffer: ArrayBuffer, authTag: ArrayBuffer ): Promise { // 1. 导入密钥 const key = await crypto.subtle.importKey( 'raw', keyBuffer, 'AES-GCM', false, ['decrypt'] ); // 2. 将认证标签附加到加密数据末尾 const combinedData = new Uint8Array(encrypted.byteLength + authTag.byteLength); combinedData.set(new Uint8Array(encrypted), 0); combinedData.set(new Uint8Array(authTag), encrypted.byteLength); // 3. 解密操作 try { const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: ivBuffer, tagLength: 128, // 默认的 GCM 标签长度 }, key, combinedData.buffer ); // 将解密后的数据转换为字符串 // return new TextDecoder().decode(decrypted); return decrypted } catch (error) { console.error('Decryption failed:', error); throw error; } } async function decryptGCM(url: string): Promise { const res = await fetch(url).catch((err) => { console.error('Error during fetch:', err) return undefined }) if (!res) return undefined const buffer = await res.arrayBuffer().catch((err) => { console.error('Error during arrayBuffer:', err) return undefined }) if (!buffer) return undefined const iv = buffer.slice(0, 12) const lenBuf = buffer.slice(buffer.byteLength - 4) const pLen = readUint32BE(lenBuf) const passwordBuffer = buffer.slice(buffer.byteLength - pLen - 4, buffer.byteLength - 4) const password = arrayBufferToBase64(passwordBuffer) const authTag = buffer.slice(buffer.byteLength - pLen - 4 - 16, buffer.byteLength - pLen - 4) const encrypted = buffer.slice(12, buffer.byteLength - pLen - 4 - 16) const key = await getKey(password).catch((err) => { console.error('Error during get key:', err) return undefined }) if (!key) return undefined const decrypted = await decryptAES_GCM(encrypted, key, iv, authTag).catch((err) => { console.error('Error during decrypt:', err) return undefined }) if (!decrypted) return undefined return new Uint8Array(decrypted) } // 解密示例 const decryptedData = await decryptGCM(withBase(props.fileUrl)).catch((err) => { console.error('Error during decrypt GCM:', err) return undefined }) if (!decryptedData) return