Encrypt and Transfer Files over websocket

I’m developing a service for personal use, where i can exchange public key between 2 clients, and transfer encrypted files. I am new to websocket, so i don’t know what i am doing wrong here so the file isn’t encrypted when received on the other end.
So what i am doing here is:
1- generating private/public rsa key
2- sending it to be stored in websocket server
3- fetch public key by the other party
4- use the public key to encrypt the aes
5- encrypt the file with aes
6- send the file
7- Try to decrypt the aes with the private key => FAILING

Client Side

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Secure File Transfer</title>
</head>
<body>
<h2>Secure File Transfer</h2>
<div>
    <label for="clientId">Your Client ID:</label>
    <input type="text" id="clientId">
    <button onclick="setClientId()">Set Client ID</button>
    <p>Client ID: <span id="displayedClientId"></span></p>
</div>
<div>
    <label for="fileInput">Select File:</label>
    <input type="file" id="fileInput">
    <button onclick="handleFileUpload()">Upload File</button>
</div>
<div>
    <label for="targetClientId">Target Client ID:</label>
    <input type="text" id="targetClientId">
</div>
<div id="status"></div>
<div id="progress"></div>

<script>
    let socket;
    let keyPair;
    let targetPublicKey;
    let clientId;
    let receivedChunks = {};
    let totalFileSize = 0;
    let aesKey; // Global variable for AES key

    async function initializeWebSocket() {
        socket = new WebSocket('ws://localhost:1111'); // WebSocket server URL

        socket.onopen = async function() {
            console.log('Connected to WebSocket server.');

            // Generate RSA key pair
            try {
                keyPair = await generateRSAKeyPair();
                setStatus('RSA key pair generated.');

                if (clientId) {
                    await sendPublicKey();
                }
            } catch (error) {
                console.error('Error generating RSA key pair:', error);
                setStatus('Error generating RSA key pair.');
            }
        };

        socket.onmessage = async function(event) {
            const message = JSON.parse(event.data);
            console.log('Received message:', message);

            if (message.type === 'publicKey') {
                if (message.clientId !== clientId) {
                    // Received the target client's public key
                    targetPublicKey = await importPublicKey(message.publicKey);
                    setStatus('Received public key from target client.');
                }
            } else if (message.type === 'file') {
                await handleIncomingFileChunk(message);
            } else if (message.type === 'error') {
                setStatus('Error: ' + message.message);
            }
        };

        socket.onclose = function() {
            console.log('Disconnected from WebSocket server.');
        };

        socket.onerror = function(error) {
            console.error('WebSocket error:', error);
            setStatus('WebSocket error: ' + error.message);
        };
    }

    function setClientId() {
        clientId = document.getElementById('clientId').value;
        document.getElementById('displayedClientId').textContent = clientId;

        if (!keyPair) {
            setStatus('Generating RSA key pair...');
            generateRSAKeyPair().then(generatedKeyPair => {
                keyPair = generatedKeyPair;
                sendPublicKey();
            }).catch(error => {
                console.error('Error generating RSA key pair:', error);
                setStatus('Error generating RSA key pair.');
            });
        } else {
            sendPublicKey();
        }
    }

    async function sendPublicKey() {
        if (!keyPair || !keyPair.publicKey) {
            setStatus('RSA key pair not generated yet.');
            return;
        }

        const publicKeyString = await exportPublicKey(keyPair.publicKey);
        socket.send(JSON.stringify({ type: 'publicKey', clientId: clientId, publicKey: publicKeyString }));
        setStatus('Public key sent to server.');
    }

    async function handleFileUpload() {
        const fileInput = document.getElementById('fileInput');
        const files = fileInput.files;
        const targetClientId = document.getElementById('targetClientId').value;

        if (!files || files.length === 0) {
            setStatus('No file selected.');
            return;
        }

        if (!targetClientId) {
            setStatus('Target client ID is required.');
            return;
        }

        if (!targetPublicKey) {
            setStatus('Requesting public key from target client...');
            socket.send(JSON.stringify({ type: 'requestPublicKey', targetClientId: targetClientId }));
            return;
        }

        const file = files[0];
        totalFileSize = file.size;
        const chunkSize = 245;
        let offset = 0;

        try {
            // Generate AES key for encrypting file chunks
            aesKey = await generateAESKey();

            // Encrypt AES key with RSA
            const encryptedAesKey = await encryptAESKeyWithRSA(aesKey, targetPublicKey);

            setStatus('Uploading file...');
            setProgress(0);

            while (offset < file.size) {
                const chunk = await readChunk(file, offset, chunkSize);
                const encryptedChunk = await encryptChunkWithAES(chunk);

                const message = {
                    type: 'file',
                    targetClientId: targetClientId,
                    filename: file.name,
                    fileType: file.type,
                    offset: offset,
                    chunkSize: encryptedChunk.length,
                    encryptedData: encryptedChunk,
                    totalFileSize: file.size,
                    aesKey: encryptedAesKey // Include encrypted AES key
                };

                socket.send(JSON.stringify(message));

                offset += chunkSize;
                const percent = Math.min((offset / file.size) * 100, 100);
                setProgress(percent);
            }

            setStatus('File uploaded successfully.');
        } catch (error) {
            console.error('Error uploading file:', error);
            setStatus('Error uploading file: ' + error.message);
        }
    }

    async function readChunk(file, offset, length) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = function(event) {
                const chunk = new Uint8Array(event.target.result);
                resolve(chunk);
            };
            reader.onerror = function(event) {
                reject(event.target.error);
            };

            const blob = file.slice(offset, offset + length);
            reader.readAsArrayBuffer(blob);
        });
    }

    async function handleIncomingFileChunk(message) {
        const { filename, fileType, offset, encryptedData, totalFileSize, aesKey } = message;
        console.log('Received file chunk:', message);

        try {

            // Decrypt AES key with RSA private key
            const decryptedAesKey = await decryptAESKeyWithRSA(aesKey);

            // Decrypt chunk data using decrypted AES key
            const decryptedBytes = await decryptChunkWithAES(encryptedData, decryptedAesKey);

            // Store decrypted chunk
            receivedChunks[offset] = new Uint8Array(decryptedBytes);

            // Check if all chunks are received
            if (offset + 245 >= totalFileSize) {
                const combinedChunks = combineChunks(receivedChunks, totalFileSize);
                handleCompleteFile({
                    filename: filename,
                    fileType: fileType,
                    combinedChunks: combinedChunks
                });
            }

            setStatus(`Received chunk for file: ${filename}`);
        } catch (error) {
            console.error('Error handling incoming file chunk:', error);
            setStatus('Error handling incoming file chunk: ' + error.message);
        }
    }


    async function generateRSAKeyPair() {
        const keyPair = await window.crypto.subtle.generateKey(
            {
                name: 'RSA-OAEP',
                modulusLength: 2048,
                publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
                hash: { name: 'SHA-256' }
            },
            true,
            ['encrypt', 'decrypt']
        );
        return keyPair;
    }

    async function exportPublicKey(publicKey) {
        const exportedKey = await window.crypto.subtle.exportKey(
            'spki',
            publicKey
        );
        const exportedKeyArray = new Uint8Array(exportedKey);
        return btoa(String.fromCharCode.apply(null, exportedKeyArray));
    }

    async function importPublicKey(publicKeyString) {
        const publicKeyBuffer = Uint8Array.from(atob(publicKeyString), c => c.charCodeAt(0));
        const importedKey = await window.crypto.subtle.importKey(
            'spki',
            publicKeyBuffer.buffer,
            {
                name: 'RSA-OAEP',
                hash: { name: 'SHA-256' }
            },
            true,
            ['encrypt']
        );
        return importedKey;
    }

    async function encryptAESKeyWithRSA(aesKey, publicKey) {
        const exportedKey = await window.crypto.subtle.exportKey(
            'raw',
            aesKey
        );

        const encryptedKey = await window.crypto.subtle.encrypt(
            {
                name: 'RSA-OAEP'
            },
            publicKey,
            exportedKey
        );

        return new Uint8Array(encryptedKey);
    }

    async function decryptAESKeyWithRSA(encryptedAesKey) {
        // Ensure keyPair is defined and has privateKey
        if (!keyPair || !keyPair.privateKey) {
            throw new Error('RSA private key not available.');
        }

        try {
            const encryptedAesKeyArray = new Uint8Array(encryptedAesKey);

            const decryptedAesKey = await window.crypto.subtle.decrypt(
                {
                    name: 'RSA-OAEP'
                },
                keyPair.privateKey,
                encryptedAesKeyArray
            );

            return decryptedAesKey;
        } catch (error) {
            console.error('Error decrypting AES key with RSA:', error);
            throw new Error('Failed to decrypt AES key with RSA.');
        }
    }


    async function generateAESKey() {
        const key = await window.crypto.subtle.generateKey(
            {
                name: 'AES-GCM',
                length: 256
            },
            true,
            ['encrypt', 'decrypt']
        );
        return key;
    }

    async function encryptChunkWithAES(chunk) {
        const iv = window.crypto.getRandomValues(new Uint8Array(12));

        const encrypted = await window.crypto.subtle.encrypt(
            {
                name: 'AES-GCM',
                iv: iv
            },
            aesKey,
            chunk
        );

        // Concatenate IV and encrypted data
        const encryptedArray = new Uint8Array(iv.byteLength + encrypted.byteLength);
        encryptedArray.set(new Uint8Array(iv), 0);
        encryptedArray.set(new Uint8Array(encrypted), iv.byteLength);

        return encryptedArray;
    }

    async function decryptChunkWithAES(encryptedChunk, aesKey) {
        const iv = encryptedChunk.slice(0, 12);
        const encryptedData = encryptedChunk.slice(12);

        const decrypted = await window.crypto.subtle.decrypt(
            {
                name: 'AES-GCM',
                iv: iv
            },
            aesKey,
            encryptedData
        );

        return new Uint8Array(decrypted);
    }

    function combineChunks(chunks, totalSize) {
        const combined = new Uint8Array(totalSize);
        let offset = 0;

        for (let chunkOffset in chunks) {
            const chunk = chunks[chunkOffset];
            combined.set(chunk, parseInt(chunkOffset));
            offset += chunk.length;
        }

        return combined;
    }

    function handleCompleteFile({ filename, fileType, combinedChunks }) {
        const blob = new Blob([combinedChunks], { type: fileType });
        downloadFile(blob, filename);
        setStatus(`File ${filename} downloaded successfully.`);
    }

    function downloadFile(blob, fileName) {
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName || 'file';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }

    function setStatus(message) {
        document.getElementById('status').textContent = message;
    }

    function setProgress(percent) {
        document.getElementById('progress').textContent = `Progress: ${percent}%`;
    }

    window.onload = initializeWebSocket;
</script>
</body>
</html>

Server Side

const WebSocket = require('ws');

// Initialize WebSocket server
const wss = new WebSocket.Server({ port: 1111 });

// Client storage
const clients = {};

// Handle incoming WebSocket connections
wss.on('connection', function connection(ws) {
  console.log('Client connected.');

  // Handle incoming messages from clients
  ws.on('message', function incoming(message) {
    try {
      const data = JSON.parse(message);
      console.log('Received message:', data);

      if (data.type === 'publicKey') {
        // Store client's public key
        clients[data.clientId] = {
          publicKey: data.publicKey,
          ws: ws,
        };
        console.log(`Stored public key for client ${data.clientId}`);
      } else if (data.type === 'requestPublicKey') {
        // Send requested client's public key to requester
        const targetClientId = data.targetClientId;
        const targetClient = clients[targetClientId];

        if (targetClient && targetClient.publicKey) {
          ws.send(JSON.stringify({ type: 'publicKey', clientId: targetClientId, publicKey: targetClient.publicKey }));
          console.log(`Sent public key of client ${targetClientId} to client ${data.clientId}`);
        } else {
          ws.send(JSON.stringify({ type: 'error', message: `Client ${targetClientId} not found or public key not available.` }));
        }
      } else if (data.type === 'file') {
        // Route file to target client
        const targetClientId = data.targetClientId;
        const targetClient = clients[targetClientId];

        if (targetClient && targetClient.ws) {
          targetClient.ws.send(JSON.stringify(data));
          console.log(`File routed to client ${targetClientId}`);
        } else {
          ws.send(JSON.stringify({ type: 'error', message: `Client ${targetClientId} not found or offline.` }));
        }
      } else {
        ws.send(JSON.stringify({ type: 'error', message: `Invalid message type or action.` }));
      }
    } catch (error) {
      console.error('Error parsing or processing message:', error);
    }
  });

  // Handle WebSocket close
  ws.on('close', function close() {
    console.log('Client disconnected.');
    // Clean up client's stored public key if exists
    Object.keys(clients).forEach(clientId => {
      if (clients[clientId].ws === ws) {
        delete clients[clientId];
        console.log(`Removed client ${clientId} from storage.`);
      }
    });
  });

  // Handle initial client connection setup
  ws.on('error', function error(err) {
    console.error('WebSocket error:', err);
  });
});

// Start WebSocket server
console.log('WebSocket server is running on port 1111');

I tried to base64 it instead of sending it as an buffer array

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật