Créer un chat crypté de bout en bout avec Seald et PubNub
Published: 2024-03-18 08:13:02

> **Cet article a été contribué par un tiers,** [**seald**](https://www.seald.io/)**, et présente une alternative au** [**chiffrement**](https://www.pubnub.com/docs/general/setup/data-security#message-encryption) **intégré** [**des messages de**](https://www.pubnub.com/docs/general/setup/data-security#message-encryption) **PubNub** **.** Il a **été publié à l'origine sur** [**Hackernoon**](https://hackernoon.com/how-to-build-an-end-to-end-encrypted-chat-with-seald-and-pubnub)**.**

* * *

Aujourd'hui, découvrons **comment construire un** **chat**[**chiffré de**](https://hackernoon.com/a-cryptographers-guide-to-end-to-end-encryption?ref=hackernoon.com) [bout en bout](https://hackernoon.com/a-cryptographers-guide-to-end-to-end-encryption?ref=hackernoon.com) avec **Pubnub** et le **SDK seald**.

👉 Un exemple entièrement fonctionnel de l'utilisation de PubNub et de Seald se trouve ici : [https://github.com/seald/pubnub-example-project](https://github.com/seald/pubnub-example-project?ref=hackernoon.com)

**Qu'est-ce que Pubnub ?**

PubNub est un service de [communication en temps réel](https://www.pubnub.com/solutions/data-streaming/) qui peut être intégré dans la plupart des applications. Fiable et évolutif, il peut être facilement intégré dans les frameworks les plus courants.

**Qu'est-ce que Seald ?**

Seald.io propose un SDK qui vous permet d'effectuer un **chiffrement de bout en bout**, avec des fonctionnalités de gestion avancées, **sans aucune connaissance cryptographique préalable.** Ce SDK peut être intégré dans des applications web, backend, mobiles ou de bureau.

Aperçu du contenu
-----------------

-   Pourquoi utiliser le chiffrement de bout en bout ?
    
-   Pourquoi utiliser Seald.io au lieu de PubNub encryption hook ?👀
    
-   Objectifs 🏆
    
-   Mise en œuvre 🧠
    
-   Ajout du chiffrement de bout en bout avec Seald 🔒💬💬
    
-   Conclusion ✅
    

**Pourquoi utiliser le chiffrement de bout en bout ? 🔒**
---------------------------------------------------------

Le chiffrement de bout en bout offre **le plus haut niveau de confidentialité et de sécurité.** Il permet de chiffrer les données sensibles dès qu'elles sont collectées. Un chiffrement précoce réduira la surface d'attaque de votre application. Un autre avantage est que vous pouvez **gérer avec** précision **les personnes autorisées à accéder aux données,** ce qui permet également de les protéger lorsqu'elles ne relèvent pas de votre champ d'action, **comme c'est le cas pour les services tiers**.

Le chiffrement de bout en bout vous permet **de garder le contrôle à tout moment,** lorsque vos données sont en transit, lorsqu'elles sont au repos et même lorsqu'elles ne sont pas entre vos mains. Il offre donc une protection bien plus large que les autres technologies de chiffrement (TLS, chiffrement du disque complet, ...).

Chaque fois que vous êtes confronté à un cas où **la** [**conformité**](https://hackernoon.com/60-stories-to-learn-about-compliance?ref=hackernoon.com) **est importante** (GDPR, HIPAA, SOC-2,...) ou que vous avez des **données sensibles** (médicales, de défense,...), le chiffrement de bout en bout est un must-have. Mais même pour des données plus courantes, il s'agit d'une bonne pratique. Une violation de données est un événement dévastateur qui devient de plus **en plus fréquent.**

**Pourquoi utiliser Seald.io plutôt que le crochet de chiffrement PubNub ? 👀**
-------------------------------------------------------------------------------

Le SDK PubNub offre un crochet de chiffrement simple, en utilisant l'argument `cipherKey` lors de l'instanciation de son SDK. En procédant ainsi, vous vous assurez que tous les messages téléchargés sont chiffrés **avant d'être envoyés.** Cependant, vous devez vous charger vous-même de la gestion des clés.

Les vulnérabilités proviennent rarement du chiffrement lui-même, mais le plus souvent **de fuites de clés** dues à des failles dans le modèle de sécurité.

Utiliser un service tiers pour votre sécurité vous permet de **ne pas avoir un seul point de défaillance.** Seald.io propose un modèle de sécurité robuste, **certifié par l'ANSSI,** avec un contrôle de gestion des accès en temps réel, la révocation et la récupération des utilisateurs, le 2FA, et plus encore.

**Objectifs 🏆**
----------------

Cet article explique comment intégrer Seald avec PubNub **étape par étape,** afin de sécuriser votre chat avec un chiffrement de bout en bout. Nous allons construire **un exemple d'application de messagerie,** avec les fonctionnalités suivantes :

-   Salles de chat individuelles et collectives.
    
-   Chaque membre dispose d'un espace de discussion dédié avec tous les autres utilisateurs.
    
-   N'importe qui peut créer un salon de discussion de groupe avec plusieurs autres utilisateurs.
    
-   Cryptage de bout en bout de tous les messages et fichiers envoyés.
    
-   Permettre la gestion de l'accès en temps réel aux chats.
    

**Mise en œuvre 🧠**
--------------------

### **Créer un compte PubNub 👤**

Pour commencer, vous aurez besoin d'**un compte PubNub,** vous pouvez vous inscrire [ici](https://admin.pubnub.com/?ref=hackernoon.com#/login). Une fois connecté sur votre tableau de bord, vous devriez voir qu'**une application de démonstration** avec un jeu de clés de démonstration a été créée :

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

**Sélectionnez l'ensemble de clés de démonstration** et faites défiler jusqu'à l'onglet de configuration. Pour notre démo, nous devons activer les permissions `Fichiers` et `Objets`. Pour la permission `Objects`, nous utiliserons les événements suivants : `User Metadata Events`, `Channel Metadata Events` etMembership`Events`.

Une fois le jeu de clés créé et configuré, **nous devons le copier dans notre frontend.**

Créons un fichier JSON dans le dossier `src/`, appelé `settings.json`. Nous utiliserons ce fichier pour toutes les clés API dont nous aurons besoin. Commençons par le jeu de clés PubNub :

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

### **Construire un chat de base avec PubNub 💬**

Nous utiliserons PubNub pour presque **toutes les tâches du** backend, qui ne s'occupera que de l'inscription/signature des utilisateurs, et utilisera un modèle utilisateur minimaliste avec seulement un ID, un nom et un email.

Du côté frontal, nous avons besoin d'une petite interface d'authentification :

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

Une fois que l'utilisateur a un compte, la première chose dont il a besoin est une instance du SDK PubNub.

**Pour identifier l'utilisateur sur Pubnub,** nous devons fournir un UUID.

Pour simplifier les choses, nous utiliserons le même identifiant que sur notre backend :

```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
})
```

Pour garder notre backend aussi simple que possible, nous utiliserons les métadonnées utilisateur de PubNub pour échanger des informations sur les utilisateurs.

**Juste après l'instanciation du SDK,** nous appelons simplement la fonction PubNub `setUIDMetadata`:

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

### **Obtenir l'état initial de l'application 🌱**

**La première** chose à faire avec PubNub est de récupérer tous les membres existants et de les stocker dans notre magasin de données local :

```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 }))
  }
})
```

Chaque salon de discussion correspondra à un canal PubNub. Nous allons également ajouter quelques métadonnées à chaque canal :

-   `ownerId`: L'ID de l'utilisateur qui a créé la salle.
    
-   `one2one`: Un booléen pour différencier les salles de messagerie directe des salles de groupe.
    
-   `archived (archivé`) : Un booléen, pour cacher un salon de groupe supprimé.
    

La métadonnée `ownerId` sera utilisée plus tard lors de l'ajout du SDK Seald. PubNub n'a pas de concept de propriété, mais Seald en a un. Il définira qui peut ajouter ou supprimer des utilisateurs d'un canal. Il s'agit en fait d'un administrateur de groupe.

Nous commencerons par **récupérer les salons de discussion existants.** Nous aurons également besoin des métadonnées du salon, nous devrons donc inclure des champs personnalisés. Ensuite, nous devons filtrer les salles archivées et envoyer le tout à notre magasin de données.

Enfin, nous `nous abonnons` au canal PubNub associé à la salle, afin de recevoir les nouveaux messages :

```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) })
```

Nous avons maintenant récupéré toutes les salles dans lesquelles nous nous trouvons. Nous avons besoin d'une dernière chose pour terminer l'initialisation de l'application : s'assurer que nous avons une salle `one2one` avec tous les autres membres, y compris ceux qui viennent de s'inscrire.

Pour chaque utilisateur nouvellement trouvé, **nous allons créer** un nouveau salon et envoyer un message de bienvenue.

Ensuite, nous définissons les métadonnées de la salle et nous nous y abonnons :

```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]
      })
    }
}
```

Une fois que tout cela est fait, l'état initial de notre application **est entièrement défini.** Cependant, nous devons le maintenir à jour.

Cela peut être fait simplement en ajoutant un écouteur d'événements pour les événements d'`adhésion`:

```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
```

Nous pouvons maintenant jeter un coup d'oeil à un salon de discussion `one2one`. Nous nous occuperons ensuite des salons de groupe.

### **Recevoir et envoyer des messages dans une salle de chat 📩**

Dans un fichier `chat.js`, nous aurons toute la logique pour afficher les messages d'un salon de discussion.

**Pour initialiser cette salle,** la seule chose à faire est de récupérer tous les messages préexistants.

Cela peut être fait simplement en connaissant l'ID de la salle :

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

Nous pouvons nous abonner au canal pour recevoir les nouveaux messages, et ajouter un écouteur pour les afficher en temps réel :

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

Pour envoyer un message, il suffit de le publier sur le canal :

```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
    }
  })
}
```

Pour envoyer un**fichier,** nous allons d'abord le télécharger sur PubNub. Ensuite, nous obtiendrons l'URI du fichier téléchargé et le publierons en tant que message dans le salon de discussion :

```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)
  }
})
```

#### **Gestion du chat de groupe 👨‍👩‍👦‍👦**

Pour créer et gérer des groupes, nous avons besoin d'une interface de sélection des utilisateurs :

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

**Une fois les membres du groupe sélectionnés,** nous pouvons créer un canal PubNub pour notre salle, puis définir les métadonnées et les membres du canal. Le code est très similaire à ce qui est fait pour les salons one2one, nous ne le répéterons donc pas ici.

Nous avons maintenant une application de chat complète, **ajoutons le cryptage de bout en bout pour chaque message !**

**Ajouter le cryptage de bout en bout avec Seald 🔒💬💬**
---------------------------------------------------------

### **Créer un compte Seald 👤**

Pour commencer avec Seald, **créez un compte d'essai gratuit** [ici](https://www.seald.io/create-sdk?ref=hackernoon.com).

En atterrissant sur le tableau de bord de Seald, quelques URL et jetons d'API sont affichés.

Obtenez les éléments suivants :

-   appId
    
-   apiURL
    
-   keyStorageURL
    

Nous ajouterons ces clés à notre `fichier 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"
}
```

**Pour pouvoir utiliser le SDK Seald,** chaque utilisateur a besoin d'une licence JWT lors de son inscription.

Ces JWT doivent être générés sur le backend en utilisant un secret et un ID secret.

Depuis la page d'atterrissage du tableau de bord, copiez le secret JWT et son ID associé dans `backend/settings.json`:

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

Lors de l'appel à l'API d'inscription, nous générerons un JWT de licence Seald et le renverrons :

```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'))
```

Pour plus d'informations à ce sujet, consultez [notre article de documentation sur les JWT](https://docs.seald.io/en/sdk/guides/jwt.html?ref=hackernoon.com#creating-a-json-web-token).

Ensuite, nous devons **installer le SDK Seald.**

**Nous devons également installer un plugin** pour identifier les utilisateurs sur le serveur Seald. Pour ce faire, nous utiliserons le paquet `sdk-plugin-ssks-password`.

Ce plugin permet une authentification simple par mot de passe de nos utilisateurs :

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

### **Instancier le SDK Seald 💡**

Ensuite, nous allons créer un fichier `seald.js.` Dans ce fichier, nous commencerons par créer une fonction permettant d'instancier le SDK Seald :

```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)]
  })
}
```

### **Créer et récupérer des identités Seald 🔑**

Dans `seald.js`, nous allons également ajouter deux fonctions : une pour créer une identité et une pour en récupérer une. **Pour créer une identité,** nous avons également besoin de la licence JWT renvoyée lors de la création du compte :

```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 })
}
```

Au cours de nos flux d'inscription et de connexion, il nous suffit d'appeler ces fonctions une fois que l'utilisateur s'est connecté.

À ce stade, chaque fois que notre utilisateur est connecté, il dispose d'un SDK Seald fonctionnel, **prêt à chiffrer et déchiffrer !**

**Attention :** Pour une sécurité adéquate, le mot de passe doit être pré-haché avant d'être envoyé au serveur d'authentification. Pour plus de détails à ce sujet, veuillez consulter le [paragraphe sur l'authentification par mot de passe dans notre documentation](https://docs.seald.io/en/sdk/guides/4-identities.html?ref=hackernoon.com#password-also-used-for-authentication).

### **Commencer à crypter et décrypter les messages 🔒🔓🔓**

Chaque salle de discussion sera associée à une `encryptionSession` sur le SDK Seald.

À chaque fois que nous créons un salon de discussion, **il nous suffit d'ajouter une ligne pour créer la session de chiffrement,** puis de l'utiliser :

```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 👋'))
  }
})
```

Notez que notre utilisateur est inclus par défaut en tant que destinataire de l'`encryptionSession`.

Lorsque nous accédons à une salle, nous devons récupérer la `session de chiff`rement correspondante. Elle peut être récupérée à partir de n'importe quel message ou fichier crypté. Une fois que nous l'avons, nous le gardons dans la référence du composant.

Nous pouvons alors simplement utiliser les fonctions `session.encryptMessage``,session.encryptFile`, `session.decryptMessage` et `session.decryptFile`.

Commençons par les messages. Pour envoyer un message :

```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... */
}
```

Et lorsque nous recevons un message :

```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]
  })
}
```

Nous utilisons également la fonction `decryptMessage` pour décrypter tous les messages déjà présents dans la session lors de l'ouverture d'une salle :

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

Et maintenant, les fichiers. Pour télécharger un fichier :

```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
})
```

Et pour décrypter un fichier :

```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 */
  }
}
```

### **Gestion des membres d'un groupe 👨‍👩‍👦**

Les chats de groupe auront également leur `encryptionSession`. Chaque fois qu'un groupe est créé, nous devons en créer un :

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

Ensuite, à **chaque fois que l'on modifie les membres du groupe,** il faudra les ajouter ou les supprimer de celui-ci :

```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
    })
  }
}
```

**Conclusion ✅**
----------------

Une fois cela fait, nous avons terminé !

Nous avons réussi à **intégrer Seald dans PubNub** avec seulement quelques lignes de code.

Maintenant que **le chat est chiffré de bout en bout,** vous pouvez assurer à vos utilisateurs que leurs données resteront confidentielles **, même en cas de violation de données**.

Comme toujours, n'**hésitez** pas à [**contacter Seald**](https://www.seald.io/discover?ref=hackernoon.com) **ou** [**PubNub**](https://support.pubnub.com/hc/en-us) si vous avez besoin de conseils supplémentaires.

Nous sommes impatients de voir ce que vous avez construit 🥳.

#### Sommaire

[Aperçu du contenuPourquoi](#h-0)[utiliser le chiffrement de bout en bout ? 🔒Pourquoi](#h-1)[utiliser Seald.io au lieu de PubNub encryption hook ? 👀Objectifs](#h-2)[🏆Implémentation](#h-3)[🧠Set up](#h-5) a[PubNub account 👤Building](#h-5) a[basic chat using PubNub 💬Getting](#h-6)[initial app state 🌱Receiving](#h-7)[and sending messages in a chat room 📩Managing](#h-8)[group chat](#h-9)[👨‍👩‍👦‍👦Adding](#h-10) chiffrement de[bout en bout](#h-10) avec Seald[💬Getting](#h-6)[initial app state 🌱Receiving](#h-7)[and sending messages in a chat room 📩Managing](#h-8)[group chat](#h-9)[👨‍👩‍👦‍👦Adding](#h-10)Seald[🔒💬Set](#h-10) up a Seald[account 👤Instantiating](#h-11)[the Seald SDK 💡Creating](#h-12)[and retrieving Seald identities 🔑Start](#h-13)[encrypting and decrypting messages 🔒🔓Managing](#h-14)[group members](#h-15)[👨‍👩‍👦Conclusion ✅](#h-16)

Comment PubNub peut-il vous aider ?
===================================

Cet article a été publié à l'origine sur [PubNub.com](https://www.pubnub.com/blog/build-an-end-to-end-encrypted-chat-with-seald-and-pubnub/).

Notre plateforme aide les développeurs à construire, fournir et gérer l'interactivité en temps réel pour les web apps, les apps mobiles et les appareils IoT.

La base de notre plateforme est le réseau de messagerie en temps réel le plus grand et le plus évolutif de l'industrie. Avec plus de 15 points de présence dans le monde, 800 millions d'utilisateurs actifs mensuels et une fiabilité de 99,999 %, vous n'aurez jamais à vous soucier des pannes, des limites de concurrence ou des problèmes de latence causés par les pics de trafic.

Découvrez PubNub
----------------

Découvrez le [Live Tour](https://www.pubnub.com/tour/introduction/) pour comprendre les concepts essentiels de chaque application alimentée par PubNub en moins de 5 minutes.

S'installer
-----------

Créez un [compte PubNub](https://admin.pubnub.com/signup/) pour un accès immédiat et gratuit aux clés PubNub.

Commencer
---------

La [documentation PubNub](https://www.pubnub.com/docs) vous permettra de démarrer, quel que soit votre cas d'utilisation ou votre [SDK](https://www.pubnub.com/docs).