Zbuduj szyfrowany czat od początku do końca za pomocą Seald i PubNub
Published: 2024-03-18 07:51:03

> **Ten artykuł został napisany przez osobę trzecią,** [**seald**](https://www.seald.io/)**, i przedstawia alternatywę dla wbudowanego** [**szyfrowania wiadomości na poziomie**](https://www.pubnub.com/docs/general/setup/data-security#message-encryption) **PubNub** **.** Został on **pierwotnie opublikowany na** [**Hackernoon**](https://hackernoon.com/how-to-build-an-end-to-end-encrypted-chat-with-seald-and-pubnub)**.**

* * *

Dziś dowiemy się, **jak zbudować**[**szyfrowany**](https://hackernoon.com/a-cryptographers-guide-to-end-to-end-encryption?ref=hackernoon.com) **czat** [end-to-end](https://hackernoon.com/a-cryptographers-guide-to-end-to-end-encryption?ref=hackernoon.com) za pomocą **Pubnub** i seald **SDK**.

W pełni działający przykład użycia PubNub i Seald można znaleźć tutaj: https: [//github.com/seald/pubnub-example-project](https://github.com/seald/pubnub-example-project?ref=hackernoon.com)

**Czym jest Pubnub?**

PubNub to usługa [komunikacji w czasie rzeczywistym](https://www.pubnub.com/solutions/data-streaming/), którą można zintegrować z większością aplikacji. Niezawodna i skalowalna, może być łatwo zintegrowana z najpopularniejszymi frameworkami.

**Co to jest Seald?**

Seald.io oferuje SDK, który pozwala na **szyfrowanie end-to-end**, z zaawansowanymi funkcjami zarządzania, **bez wcześniejszej wiedzy kryptograficznej.** Ten SDK można zintegrować z aplikacjami internetowymi, backendowymi, mobilnymi lub desktopowymi.

Przegląd zawartości
-------------------

-   Dlaczego warto korzystać z szyfrowania end-to-end?
    
-   Dlaczego warto używać Seald.io zamiast haka szyfrującego PubNub?
    
-   Cele 🏆
    
-   Wdrożenie 🧠
    
-   Dodanie szyfrowania end-to-end za pomocą Seald 🔒💬
    
-   Podsumowanie ✅
    

**Dlaczego warto używać szyfrowania end-to-end? 🔒**
----------------------------------------------------

Szyfrowanie typu end-to-end oferuje **najwyższy poziom prywatności i bezpieczeństwa.** Umożliwia szyfrowanie wrażliwych danych natychmiast po ich zebraniu. Wczesne szyfrowanie zmniejsza powierzchnię ataku aplikacji. Kolejną zaletą jest to, że możesz precyzyjnie **zarządzać tym, kto może uzyskać dostęp** do danych. Będzie to również chronić, gdy nie jest to w twoim zakresie, **na przykład usługi innych firm**.

Szyfrowanie typu end-to-end pozwala **zachować kontrolę przez cały czas,** gdy dane są przesyłane, gdy są w spoczynku, a nawet gdy nie są w zasięgu ręki. W ten sposób oferuje znacznie szerszą ochronę niż inne technologie szyfrowania (TLS, szyfrowanie pełnodyskowe, ...).

Za każdym razem, gdy masz do czynienia z przypadkiem, w którym [**zgodność**](https://hackernoon.com/60-stories-to-learn-about-compliance?ref=hackernoon.com) **jest ważna** (RODO, HIPAA, SOC-2, ...) lub gdy masz **wrażliwe dane** (medyczne, obronne, ...), szyfrowanie end-to-end jest koniecznością. Ale nawet w przypadku bardziej powszechnych danych jest to dobra praktyka. Naruszenie bezpieczeństwa danych jest niszczycielskim wydarzeniem, które staje się **coraz częst**sze.

**Dlaczego warto używać Seald.io zamiast haka szyfrującego PubNub? 👀**
-----------------------------------------------------------------------

PubNub SDK oferuje prosty hak szyfrujący, wykorzystujący argument `cipherKey` podczas tworzenia instancji SDK. Zapewni to, że wszystkie przesłane wiadomości zostaną zaszyfrowane **przed wysłaniem.** Jednak zarządzanie kluczami należy wykonać samodzielnie.

Luki w zabezpieczeniach rzadko wynikają z samego szyfrowania, ale najczęściej **z wycieku kluczy** przez luki w modelu bezpieczeństwa.

Seald.io proponuje solidny model bezpieczeństwa, certyfikowany **przez ANSSI,** z kontrolą zarządzania dostępem w czasie rzeczywistym, odwoływaniem i odzyskiwaniem użytkowników, 2FA i nie tylko **.**

**Cele 🏆**
-----------

Ten artykuł wyjaśnia, jak **krok po kroku** zintegrować Seald z PubNub, aby zabezpieczyć czat za pomocą szyfrowania end-to-end. Zbudujemy **przykładową aplikację do przesyłania wiadomości** z następującymi funkcjami:

-   Czaty indywidualne i grupowe.
    
-   Każdy użytkownik ma dedykowany pokój czatu z każdym innym użytkownikiem.
    
-   Każdy może utworzyć czat grupowy z wieloma innymi użytkownikami.
    
-   Szyfrowanie end-to-end każdej wysyłanej wiadomości i pliku.
    
-   Zarządzanie dostępem do czatów w czasie rzeczywistym.
    

**Wdrożenie 🧠**
----------------

### **Założenie konta PubNub 👤**

Aby rozpocząć, będziesz potrzebować **konta PubNub.** Możesz zarejestrować się [tutaj](https://admin.pubnub.com/?ref=hackernoon.com#/login). Po zalogowaniu się na pulpicie nawigacyjnym powinieneś zobaczyć, że została utworzona **aplikacja demonstracyjna** z demonstracyjnym zestawem kluczy:

![](https://www.pubnub.com/cdn/3prze68gbwl1/7dlHV6HrK8ycTndYN8kN60/b8c5c4ed358737006494e485ae750867/YIMSJwDGhsOOhi9zHncV7Nc4OJG2-2023-09-20T13_43_30.768Z-sc4346be8h3z6uph34s3t8gi.jpg)

Wybierz**demonstracyjny** zestaw kluczy i przewiń do zakładki konfiguracji. Dla naszego demo musimy aktywować uprawnienia `Files` i `Objects`. W przypadku uprawnienia `Object` użyjemy następujących zdarzeń: `User Metadata Events`, `Channel Metadata Events` iMembership`Events`.

Po utworzeniu i skonfigurowaniu zestawu kluczy musimy **skopiować go do naszego frontendu**.

Utwórzmy plik JSON w folderze `src/` o nazwie `settings.json`. Będziemy używać tego pliku dla wszystkich kluczy API, których będziemy potrzebować. Zaczynamy od zestawu kluczy PubNub:

```js
{
  "PUBNUB_PUB_KEY": "pub-c-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "PUBNUB_SUB_KEY": "sub-c-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
```

### **Tworzenie podstawowego czatu przy użyciu PubNub 💬**

Będziemy używać PubNub do prawie **każdego zadania backendu.** Nasz backend będzie obsługiwał tylko rejestrację / logowanie użytkowników i używał minimalistycznego modelu użytkownika z tylko identyfikatorem, nazwą i adresem e-mail.

Z przodu potrzebujemy małego interfejsu uwierzytelniania:

![](https://www.pubnub.com/cdn/3prze68gbwl1/4QeRZBwCwvCKNd643LRd80/51d262b0ec0bc0af901139b02788cc13/YIMSJwDGhsOOhi9zHncV7Nc4OJG2-2023-09-20T13_43_30.784Z-gzbwgy4u8frc04p3yn3tmb87.jpg)

Gdy użytkownik ma już konto, pierwszą rzeczą, której potrzebuje, jest instancja PubNub SDK.

**Aby zidentyfikować użytkownika w Pub** nub, musimy podać identyfikator UUID.

Aby zachować prostotę, użyjemy tego samego identyfikatora, co w naszym backendzie:

```js
/* frontend/src/App.js */
import settings from './settings.json' // our settings file for API keys
/*
...
*/
const pubnub = new PubNub({
    publishKey: settings.PUBNUB_PUB_KEY,
    subscribeKey: settings.PUBNUB_SUB_KEY,
    uuid: currentUser.id
})
```

Aby nasz backend był tak prosty, jak to tylko możliwe, użyjemy metadanych użytkownika PubNub do wymiany informacji o użytkownikach.

**Tuż po instancji SDK,** po prostu wywołujemy funkcję PubNub `setUUIDMetadata`:

```js
/* frontend/src/App.js */
await pubnub.objects.setUUIDMetadata({
  uuid: currentUser.id,
  data: {
    email: currentUser.emailAddress,
    name: currentUser.name
  }
})
```

### **Pobieranie początkowego stanu aplikacji 🌱**

**Pierwszą rzeczą** do zrobienia z PubNub jest pobranie wszystkich istniejących członków i przechowywanie ich w naszym lokalnym magazynie danych:

```js
/* frontend/src/App.js */
const existingMembers = await pubnub.objects.getAllUUIDMetadata()
dispatch({
  type: SET_USERS,
  payload: {
    users: existingMembers.data.map(u => new User({ id: u.id, name: u.name, emailAddress: u.email }))
  }
})
```

Każdy pokój rozmów będzie odpowiadał kanałowi PubNub. Dodamy również pewne metadane do każdego kanału:

-   `ownerId`: Identyfikator użytkownika, który utworzył pokój.
    
-   `one2one`: Wartość logiczna rozróżniająca pokoje do bezpośredniej komunikacji i pokoje grupowe.
    
-   `archived`: Wartość logiczna, aby ukryć usunięty pokój grupowy.
    

Metadane `ownerId` zostaną użyte później podczas dodawania Seald SDK. PubNub nie ma koncepcji własności, ale Seald tak. Zdefiniuje on, kto może dodawać lub usuwać użytkowników z kanału. Zasadniczo definiuje administratora grupy.

Zaczniemy od **pobrania istniejących pokojów rozmów**. Będziemy również potrzebować metadanych pokoju, więc musimy dołączyć niestandardowe pola. Następnie musimy odfiltrować zarchiwizowane pokoje i wysłać wszystko do naszego magazynu danych.

Na koniec `subskrybujemy` kanał PubNub powiązany z pokojem, dzięki czemu będziemy otrzymywać nowe wiadomości:

```js
/* frontend/src/App.js */
// Retrieve rooms of which we are members
const memberships = await pubnub.objects.getMemberships({
  include: {
    customChannelFields: true
  }
})
const knownRooms = []
// For each room, retrieve room members
for (const room of memberships.data.filter(r => !r.channel.custom.archived)) {
  const roomMembers = await pubnub.objects.getChannelMembers({ channel: room.channel.id })
  knownRooms.push(new Room({
    id: room.channel.id,
    name: room.channel.name,
    users: roomMembers.data.map(u => u.uuid.id),
    ownerId: room.channel.custom.ownerId,
    one2one: room.channel.custom.one2one
  }))
}
// Store rooms in our data store
dispatch({
  type: SET_ROOMS,
  payload: {
    rooms: knownRooms
  }
})
// Subscribe to channels to get new messages
pubnub.subscribe({ channels: knownRooms.map(r => r.id) })
```

Teraz pobraliśmy wszystkie pokoje, w których się znajdujemy. Potrzebujemy jeszcze jednej rzeczy, aby zakończyć inicjalizację aplikacji: upewnić się, że mamy pokój `one2one` z każdym innym członkiem, w tym z nowo zarejestrowanymi.

Dla każdego nowo znalezionego użytkownika **utworzymy** nowy pokój i wyślemy wiadomość powitalną.

Następnie ustawimy metadane pokoju i zasubskrybujemy go:

```js
/* frontend/src/App.js */
// Ensure that we have a one2one room with everyone
const one2oneRooms = knownRooms.filter(r => r.one2one)
for (const m of existingMembers.data.filter(u => u.id!== currentUser.id)) {
    if (!one2oneRooms.find(r => r.users.includes(m.id))) {
      // New user found: generating a new one2one room
      const newRoomId = PubNub.generateUUID()
      const newRoom = new Room({
            id: newRoomId,
            users: [currentUser.id, m.id],
            one2one: true,
            name: m.name,
            ownerId: currentUser.id
          })
      // Add the new room to our local list
      dispatch({
        type: EDIT_OR_ADD_ROOM,
        payload: {
          room: new Room({
            id: newRoomId,
            users: [currentUser.id, m.id],
            one2one: true,
            name: m.name, ownerId: currentUser.id
          })
        }
      })
      // Publish a "Hello" message in the room
      await pubnub.publish({
        channel: newRoomId,
        message: {
          type: 'message',
          data: (await sealdSession.encryptMessage('Hello 👋'))
        }
      })
      // Subscribe to the new room
      pubnub.subscribe({ channels: [newRoomId] })
      await pubnub.objects.setChannelMetadata({
        channel: newRoomId,
        data: {
          name: 'one2one',
          custom: {
            one2one: true,
            ownerId: currentUser.id,
          },
        }
      })
      await pubnub.objects.setChannelMembers({
        channel: newRoomId,
        uuids: [currentUser.id, m.id]
      })
    }
}
```

Gdy to wszystko zostanie zrobione, nasz początkowy stan aplikacji **jest w pełni zdefiniowany.** Musimy go jednak aktualizować.

Można to zrobić po prostu dodając detektor zdarzeń dla zdarzeń `członkostwa`:

```js
/* frontend/src/App.js */
pubnub.addListener({
  objects: async function(objectEvent) {
    if (objectEvent.message.type === 'membership') {
      if (objectEvent.message.event === 'delete') { // User is removed from a room
        /*
        Removing the room from store...
        */
      }
      if (objectEvent.message.event === 'set') { // User is added to a room
        const metadata = await pubnub.objects.getChannelMetadata({ channel: objectEvent.message.data.channel.id })
        const roomMembers = (await pubnub.objects.getChannelMembers({ channel: objectEvent.message.data.channel.id })).data.map(u => u.uuid.id)
        /*
        Adding new room to store + subscribing to new room channel...
        */
      }
    }
  }
})
pubnub.subscribe({ channels: [currentUser.id] }) // channel on which events concerning the current user are published
```

Możemy teraz przyjrzeć się samemu czatowi `one2one`. Następnie zajmiemy się pokojami grupowymi.

### **Odbieranie i wysyłanie wiadomości w pokoju czatu 📩**

W pliku `chat.js` będziemy mieli całą logikę do wyświetlania wiadomości z pokoju czatu.

**Aby zainicjować ten** pokój, jedyną rzeczą, którą musimy zrobić, jest pobranie wszystkich wcześniej istniejących wiadomości.

Można to zrobić po prostu znając identyfikator pokoju:

```js
/* frontend/src/components/Chat.jsx */
const fetchedMessages = (await pubnub.fetchMessages({ channels: [currentRoomId] })).channels[currentRoomId]
```

Możemy zasubskrybować kanał, aby otrzymywać nowe wiadomości i dodać słuchacza, aby wyświetlać je w czasie rzeczywistym:

```js
/* frontend/src/components/Chat.jsx */
pubnub.addListener({ message: handleReceiveMessage })
pubnub.subscribe({ channels: [currentRoomId] })
```

Aby wysłać wiadomość, musimy po prostu opublikować ją na kanale:

```js
/* frontend/src/components/Chat.jsx */
const handleSubmitMessage = async e => {
  /* Some checks that the room is in a correct state... */
  await pubnub.publish({
    channel: state.room.id,
    message: {
      type: 'message',
      data: state.message
    }
  })
}
```

**Aby wysłać plik,** najpierw prześlemy go do PubNub. Następnie pobierzemy URI przesłanego pliku i opublikujemy go jako wiadomość na czacie:

```js
/* frontend/src/components/UploadButton.jsx */
// Upload Encrypted file
const uploadData = await pubnub.sendFile({
  channel: room.id,
  file: myFile,
  storeInHistory: false
})
const fileURL = await pubnub.getFileUrl({ id: uploadData.id, name: uploadData.name, channel: room.id })
await pubnub.publish({
  channel: state.room.id,
  message: {
    type: 'file',
    url: fileURL,
    fileName: await sealdSession.encryptMessage(selectedFiles[0].name)
  }
})
```

#### **Zarządzanie czatem grupowym 👨‍👩‍👦‍👦**

Aby tworzyć grupy i zarządzać nimi, będziemy potrzebować interfejsu do wybierania użytkowników:

![](https://www.pubnub.com/cdn/3prze68gbwl1/5eUvBiTJXw901zb2ItefN0/2189f12071c272cc34192ca26ce2339f/YIMSJwDGhsOOhi9zHncV7Nc4OJG2-2023-09-20T13_43_30.789Z-qpr0csdfsonvkr3haucsetki.jpg)

Po wybraniu**członków grupy** możemy utworzyć kanał PubNub dla naszego pokoju, a następnie ustawić metadane i członkostwo dla kanału. Kod jest bardzo podobny do tego, co robi się dla pokoi one2one, więc nie będziemy go tutaj powtarzać.

Mamy teraz pełną aplikację czatu. Dodajmy szyfrowanie end-to-end **dla każdej wiadomości!**

**Dodawanie szyfrowania end-to-end z Seald 🔒💬**
-------------------------------------------------

### **Skonfiguruj konto Seald 👤**

Aby rozpocząć korzystanie z Seald, **utwórz bezpłatne konto próbne** [tutaj](https://www.seald.io/create-sdk?ref=hackernoon.com)

Po wylądowaniu na pulpicie nawigacyjnym Seald wyświetlanych jest kilka adresów URL i tokenów API.

Pobierz następujące elementy:

-   appId
    
-   apiURL
    
-   keyStorageURL
    

Dodamy te klucze do naszego pliku `settings.json`:

```js
{
  "PUBNUB_PUB_KEY": "pub-c-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "PUBNUB_SUB_KEY": "sub-c-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "SEALD_APP_ID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "SEALD_API_URL": "https://api.staging-0.seald.io",
  "SEALD_KEYSTORAGE_URL": "https://ssks.staging-0.seald.io"
}
```

**Aby móc korzystać z Seald SDK,** każdy użytkownik potrzebuje licencji JWT podczas rejestracji.

Te JWT muszą zostać wygenerowane na zapleczu przy użyciu tajnego i tajnego identyfikatora.

Ze strony docelowej pulpitu nawigacyjnego skopiuj sekret JWT i powiązany z nim identyfikator w `backend/settings.json`:

```js
{
  "SEALD_JWT_SECRET_ID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "SEALD_JWT_SECRET": "XXXXXXXXXXXXXXXXXXXXXXXX"
}
```

Podczas wywołania API rejestracji wygenerujemy JWT licencji Seald i zwrócimy go z powrotem:

```js
/* backend/routes/account.js */
const token = new SignJWT({
  iss: settings.SEALD_JWT_SECRET_ID,
  jti: uuidv4(), /// Random string with enough entropy to never repeat.
  iat: Math.floor(Date.now() / 1000), // JWT valid only for 10 minutes. `Date.now()` returns the  in milliseconds, this needs it in seconds.
  scopes: [3], // PERMISSION_JOIN_TEAM
  join_team: true
})
  .setProtectedHeader({ alg: 'HS256' })
const signupJWT = await token.sign(Buffer.from(settings.SEALD_JWT_SECRET, 'ascii'))
```

Więcej informacji na ten temat można znaleźć w [naszym artykule na temat JWT](https://docs.seald.io/en/sdk/guides/jwt.html?ref=hackernoon.com#creating-a-json-web-token).

Następnie musimy zainstalować Seald **SDK**.

**Musimy również zainstalować wtyczkę** do identyfikacji użytkowników na serwerze Seald. W tym celu użyjemy pakietu `sdk-plugin-ssks-password`.

Wtyczka ta pozwala na proste uwierzytelnianie hasłem naszych użytkowników:

```js
npm i -S @seald-io/sdk @seald-io/sdk-plugin-ssks-password
```

### **Instantiating the Seald SDK 💡**

Następnie utworzymy plik `seald.js.` W tym pliku zaczniemy od utworzenia funkcji do instancjonowania Seald SDK:

```js
/* frontend/src/services/seald.js */
import SealdSDK from '@seald-io/sdk-web'
import SealdSDKPluginSSKSPassword from '@seald-io/sdk-plugin-ssks-password'
import settings from './settings.json'
let sealdSDKInstance = null
const instantiateSealdSDK = async () => {
  sealdSDKInstance = SealdSDK({
    appId: settings.SEALD_APP_ID,
    apiURL: settings.SEALD_API_URL,
    plugins: [SealdSDKPluginSSKSPassword(settings.SEALD_KEYSTORAGE_URL)]
  })
}
```

### **Tworzenie i pobieranie tożsamości Seald 🔑**

W seald `.`js dodamy również dwie funkcje: jedną do tworzenia tożsamości i jedną do jej pobierania. **Aby utworzyć tożsamość,** potrzebujemy również licencji JWT zwróconej podczas tworzenia konta:

```js
/* frontend/src/services/seald.js */
export const createIdentity = async ({ userId, password, signupJWT }) => {
  await instantiateSealdSDK()
  await sealdSDKInstance.initiateIdentity({ signupJWT })
  await sealdSDKInstance.ssksPassword.saveIdentity({ userId, password })
}
export const retrieveIdentity = async ({ userId, password }) => {
  await instantiateSealdSDK()
  await sealdSDKInstance.ssksPassword.retrieveIdentity({ userId, password })
}
```

Podczas naszych przepływów rejestracji i logowania wystarczy wywołać te funkcje po zalogowaniu się użytkownika.

W tym momencie, za każdym razem, gdy nasz użytkownik jest połączony, ma działający Seald SDK, **gotowy do szyfrowania i deszyfrowania!**

**Ostrzeżenie:** Aby zapewnić odpowiednie bezpieczeństwo, hasło powinno zostać wstępnie zaszyfrowane przed wysłaniem do serwera uwierzytelniającego. Więcej szczegółów na ten temat można znaleźć w [akapicie dotyczącym uwierzytelniania haseł w naszej dokumentacji](https://docs.seald.io/en/sdk/guides/4-identities.html?ref=hackernoon.com#password-also-used-for-authentication).

### **Rozpocznij szyfrowanie i odszyfrowywanie wiadomości 🔒🔓**

Każdy pokój rozmów będzie powiązany z `encryptionSession` w Seald SDK.

Za każdym razem, gdy tworzymy pokój rozmów, musimy **po prostu dodać jedną linię, aby utworzyć sesję szyfrowania,** a następnie jej użyć:

```js
/* frontend/src/App.js */
// Create a Seald session
const sealdSession = await getSealdSDKInstance().createEncryptionSession(
  { userIds: [currentUser.id, m.id] },
  { metadata: newRoomId }
)
// Publish a "Hello" message in the room
await pubnub.publish({
  channel: newRoomId,
  message: {
    type: 'message',
    data: (await sealdSession.encryptMessage('Hello 👋'))
  }
})
```

Zauważ, że nasz użytkownik jest domyślnie uwzględniony jako odbiorca encryptionSession `.`

Uzyskując dostęp do pokoju, musimy uzyskać odpowiednią `encryptionSession`. Można ją pobrać z dowolnej zaszyfrowanej wiadomości lub pliku. Gdy już ją uzyskamy, zachowamy ją w referencji do komponentu.

Następnie możemy po prostu użyć funkcji `session.encryptMessage``, session.encryptFile`, `session.decryptMessage` i `session.decryptFile`.

Zacznijmy od wiadomości. Aby wysłać wiadomość:

```js
/* frontend/src/components/Chat.js */
const handleSubmitMessage = async m => {
  /* Some validation that we are in a valid room... */
  // if there is no encryption session set in cache yet, create one
  // (should never happen, as a "Hello" is sent on room creation)
  if (!sealdSessionRef.current) {
    sealdSessionRef.current = await getSealdSDKInstance().createEncryptionSession(
      { userIds: state.room.users },
      { metadata: state.room.id }
    )
  }
  // use the session to encrypt the message we are trying to send
  const encryptedMessage = await sealdSessionRef.current.encryptMessage(state.message)
  // publish the encrypted message to pubnub
  await pubnub.publish({
    channel: state.room.id,
    message: {
      type: 'message',
      data: encryptedMessage
    }
  })
  /* Some cleanup... */
}
```

I kiedy otrzymujemy wiadomość:

```js
/* frontend/src/components/Chat.js */
const decryptMessage = async m => {
  /* Filter out files... */
  let encryptedData = m.message.data
  if (!sealdSessionRef.current) { // no encryption session set in cache yet
    // we try to get it by parsing the current message
    sealdSessionRef.current = await getSealdSDKInstance().retrieveEncryptionSession({ encryptedMessage: encryptedData })
    // now that we have a session loaded, let's decrypt
  }
  const decryptedData = await sealdSessionRef.current.decryptMessage(encryptedData)
  // we have successfully decrypted the message
  return {
    ...m,
    uuid: m.uuid || m.publisher,
    value: decryptedData
  }
  /* Some error handling... */
}
/* Other stuff... */
const handleReceiveMessage = async m => {
  const decryptedMessage = await decryptMessage(m)
  setState(draft => {
    draft.messages = [...draft.messages, decryptedMessage]
  })
}
```

Używamy również funkcji `decryptMessage`, aby odszyfrować wszystkie wiadomości już znajdujące się w sesji podczas otwierania pokoju:

```js
/* frontend/src/components/Chat.js */
const fetchedMessages = (await pubnub.fetchMessages({ channels: [currentRoomId] })).channels[currentRoomId]
const clearMessages = fetchedMessages ? await Promise.all(fetchedMessages.map(decryptMessage)) : []
```

A teraz pliki. Aby przesłać plik:

```js
/* frontend/src/components/UploadButton.js */
// Encrypt file
const encryptedBlob = await sealdSession.encryptFile(
  selectedFiles[0],
  selectedFiles[0].name,
  { fileSize: selectedFiles[0].size }
)
const encryptedFile = new File([encryptedBlob], selectedFiles[0].name)
// Upload Encrypted file
const uploadData = await pubnub.sendFile({
  channel: room.id,
  file: encryptedFile,
  storeInHistory: false
})
```

I aby odszyfrować plik:

```js
/* frontend/src/components/Message.js */
const onClick = async () => {
  if (state.data.type === 'file') {
    const response = await fetch(state.data.url)
    const encryptedBlob = await response.blob()
    const { data: clearBlob, filename } = await sealdSession.decryptFile(encryptedBlob)
    const href = window.URL.createObjectURL(clearBlob)
    /* Create an <a> element and simulate a click on it to download the created objectURL */
  }
}
```

### **Zarządzanie członkami grupy 👨‍👩‍👦**

Czaty grupowe również będą miały swoją `encryptionSession`. Za każdym razem, gdy tworzona jest grupa, musimy ją utworzyć:

```js
/* frontend/src/components/ManageDialogRoom.js.js */
// To create the encryptionSession
const sealdSession = await getSealdSDKInstance().createEncryptionSession(
  { userIds: dialogRoom.selectedUsersId },
  { metadata: newRoomId }
)
```

Następnie **za każdym razem, gdy modyfikujemy członków** grupy, będziemy musieli ich dodać lub usunąć:

```js
/* frontend/src/components/ManageDialogRoom.js.js */
// we compare old and new members to figure out which ones were just added or removed
const usersToRemove = dialogRoom.room.users.filter(id => !dialogRoom.selectedUsersId.includes(id))
const usersToAdd = dialogRoom.selectedUsersId.filter(id => !dialogRoom.room.users.includes(id))
if (usersToAdd.length > 0) {
  // for every added user, add them to the Seald session
  await dialogRoom.sealdSession.addRecipients({ userIds: usersToAdd })
  // then add them to the pubnub channel
  await pubnub.objects.setChannelMembers({
    channel: dialogRoom.room.id,
    uuids: usersToAdd
  })
}
if (usersToRemove.length > 0) {
  // for every removed user, revoke them from the Seald session
  await dialogRoom.sealdSession.revokeRecipients({ userIds: usersToRemove })
  // then remove them from the pubnub channel
  for (const u of usersToRemove) {
    await pubnub.objects.removeMemberships({
      channels: [dialogRoom.room.id],
      uuid: u
    })
  }
}
```

**Wniosek ✅**
-------------

Gdy to zrobimy, jesteśmy skończeni!

Udało nam się **zintegrować Seald z PubNub** za pomocą zaledwie kilku linijek kodu.

Teraz, gdy czat jest **szyfrowany od końca do końca,** możesz zapewnić swoich użytkowników, że ich dane pozostaną poufne, **nawet w przypadku naruszenia danych**.

Jak zawsze, nie **wahaj** się [**skontaktować z Seald**](https://www.seald.io/discover?ref=hackernoon.com) **lub** [**skontaktuj się z PubNub**](https://support.pubnub.com/hc/en-us) , jeśli potrzebujesz dodatkowych wskazówek.

Nie możemy się doczekać, aby zobaczyć, co zbudowałeś 🥳.

#### Spis treści

[Przegląd treści](#h-0)Dlaczego warto stosować[szyfrowanie typu end-to-end? Dlaczego](#h-1) warto[używać Seald.io zamiast haka szyfrującego PubNub? Cele](#h-2)[🏆 Wdrożenie](#h-3)[🧠 Kon](#h-5)figuracja[konta PubNub 👤 Tworzenie](#h-5)[podstawowego czatu przy użyciu PubNub 💬 Uzyskiwanie](#h-6)[początkowego stanu aplikacji 🌱 Odbieranie](#h-7)[i wysyłanie wiadomości w pokoju czatu 📩 Zarządzanie](#h-8)[czatem grupowym](#h-9)[👨‍👩‍👦‍👦Adding](#h-10) szyfrowanie[end-to-end](#h-10) za pomocą Seald 🔒 Dlaczego warto używać Seald do szyfrowania end-to-end?[szyfrowanie end-to-end z Seald 🔒💬Założenie](#h-10)[konta Seald 👤Instytucjonalizacja](#h-11)[Seald SDK 💡Tworzenie](#h-12)[i pobieranie tożsamości Seald 🔑Rozpoczęcie](#h-13)[szyfrowania i deszyfrowania wiadomości 🔒🔓Zarządzanie](#h-14)[członkami](#h-15) grupy[👨‍👩‍👦Conclusion ✅](#h-16)

Jak PubNub może ci pomóc?
=========================

Ten artykuł został pierwotnie opublikowany na [PubNub.com](https://www.pubnub.com/blog/build-an-end-to-end-encrypted-chat-with-seald-and-pubnub/)

Nasza platforma pomaga programistom tworzyć, dostarczać i zarządzać interaktywnością w czasie rzeczywistym dla aplikacji internetowych, aplikacji mobilnych i urządzeń IoT.

Fundamentem naszej platformy jest największa w branży i najbardziej skalowalna sieć przesyłania wiadomości w czasie rzeczywistym. Dzięki ponad 15 punktom obecności na całym świecie obsługującym 800 milionów aktywnych użytkowników miesięcznie i niezawodności na poziomie 99,999%, nigdy nie będziesz musiał martwić się o przestoje, limity współbieżności lub jakiekolwiek opóźnienia spowodowane skokami ruchu.

Poznaj PubNub
-------------

Sprawdź [Live Tour](https://www.pubnub.com/tour/introduction/), aby zrozumieć podstawowe koncepcje każdej aplikacji opartej na PubNub w mniej niż 5 minut.

Rozpocznij konfigurację
-----------------------

Załóż [konto](https://admin.pubnub.com/signup/) PubNub, aby uzyskać natychmiastowy i bezpłatny dostęp do kluczy PubNub.

Rozpocznij
----------

[Dokumenty](https://www.pubnub.com/docs) PubNub pozwolą Ci rozpocząć pracę, niezależnie od przypadku użycia lub [zestawu SDK](https://www.pubnub.com/docs).