| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- 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<ArrayBuffer> {
- 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<ArrayBuffer> {
- // 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<Uint8Array | undefined> {
- 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
|