import { PUBLIC_KEY_PEM } from './public'; function arrayBufferToBase64(buffer: ArrayBuffer) { let binary = ''; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); } function importPublicKeyFromString(pem: string) { const pemHeader = '-----BEGIN PUBLIC KEY-----'; const pemFooter = '-----END PUBLIC KEY-----'; const pemContents = pem .substring(pem.indexOf(pemHeader) + pemHeader.length, pem.indexOf(pemFooter)) .replace(/\s+/g, ''); const binaryDerString = window.atob(pemContents); const binaryDer = new Uint8Array(binaryDerString.length); for (let i = 0; i < binaryDerString.length; i++) { binaryDer[i] = binaryDerString.charCodeAt(i); } return binaryDer.buffer; } interface ReportBody { eventType: string; gameInfo: { gameId: string; sessionId: string; }; } export async function generateEncryptedPayload(payload: ReportBody) { const crypto = window.crypto; const subtle = crypto.subtle; const keyBuffer = importPublicKeyFromString(PUBLIC_KEY_PEM); const publicKey = await subtle.importKey( 'spki', keyBuffer, { name: 'RSA-OAEP', hash: 'SHA-1' }, false, ['encrypt'] ); const aesKeyRaw = crypto.getRandomValues(new Uint8Array(32)); const aesKeyObj = await subtle.importKey('raw', aesKeyRaw, 'AES-GCM', false, ['encrypt']); const encryptedAesKeyBuffer = await subtle.encrypt( { name: 'RSA-OAEP' }, publicKey, aesKeyRaw ); const k = arrayBufferToBase64(encryptedAesKeyBuffer); const timestamp = Date.now(); const payloadJson = { timestamp, data: payload }; const encoder = new TextEncoder(); const payloadBytes = encoder.encode(JSON.stringify(payloadJson)); const iv = crypto.getRandomValues(new Uint8Array(12)); const ciphertextWithTagBuffer = await subtle.encrypt( { name: 'AES-GCM', iv: iv, tagLength: 128 }, aesKeyObj, payloadBytes ); const ciphertextWithTag = new Uint8Array(ciphertextWithTagBuffer); const finalPayloadBuffer = new Uint8Array(iv.length + ciphertextWithTag.length); finalPayloadBuffer.set(iv, 0); // 先放 IV finalPayloadBuffer.set(ciphertextWithTag, iv.length); // 再放 Ciphertext + Tag const encryptedPayload = arrayBufferToBase64(finalPayloadBuffer.buffer); const data = { k: k, v: '1.0', payload: encryptedPayload }; return data; }