# Canaux chiffrés

Un canal dont le nom commence par `private-encrypted-` est chiffré de bout en bout. Votre serveur scelle chaque charge utile avant de la publier, et les clients navigateur autorisés la déchiffrent avec une clé provenant de votre endpoint d'autorisation. Le edge Realtime et les intermédiaires réseau ne voient que du texte chiffré.

Générez et stockez une clé maître de 32 octets. La clé maître n'apparaît jamais dans une requête API Realtime, et le préfixe du nom de canal active la fonctionnalité. Bird ne peut pas récupérer une clé perdue, et les charges utiles scellées avec cette clé restent illisibles après son remplacement.

Les canaux chiffrés utilisent le même endpoint et la même signature que les [canaux privés](/docs/guides/realtime/private-channels). La réponse d'autorisation inclut également la clé de déchiffrement dérivée du canal sous la forme `shared_secret`. Refuser un abonnement empêche ce client de recevoir la clé.

## Générer une clé maître

Générez 32 octets aléatoires, encodez-les en base64 et stockez la valeur comme le secret de l'application :

```bash
openssl rand -base64 32
```

Transmettez-la à votre serveur SDK dans la configuration realtime, à côté de la clé et du secret de l'application.

## Publier un événement chiffré

Le serveur SDK détecte le préfixe du canal, dérive sa clé à partir de la clé maître et scelle la charge utile JSON localement. La requête de publication contient l'enveloppe scellée.

```typescript
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({
  apiKey: process.env.BIRD_API_KEY,
  realtime: {
    key: process.env.BIRD_REALTIME_KEY,
    secret: process.env.BIRD_REALTIME_SECRET,
    encryptionMasterKey: process.env.BIRD_REALTIME_MASTER_KEY,
  },
});

await bird.realtime.publish("rap_01krdgeqcxet5s7t44vh8rt9mg", {
  event: "order.updated",
  channels: ["private-encrypted-orders"],
  data: { order_id: "ord_123", status: "shipped" },
});
```

```python
from bird import Bird

client = Bird(
    realtime_key=os.environ["BIRD_REALTIME_KEY"],
    realtime_secret=os.environ["BIRD_REALTIME_SECRET"],
    realtime_encryption_master_key=os.environ["BIRD_REALTIME_MASTER_KEY"],
)

client.realtime.publish(
    "rap_01krdgeqcxet5s7t44vh8rt9mg",
    event="order.updated",
    channels=["private-encrypted-orders"],
    data={"order_id": "ord_123", "status": "shipped"},
)
```

```go
client, err := bird.NewClient(
	option.WithAPIKey(os.Getenv("BIRD_API_KEY")),
	option.WithRealtimeCredentials(os.Getenv("BIRD_REALTIME_KEY"), os.Getenv("BIRD_REALTIME_SECRET")),
	option.WithRealtimeEncryptionMasterKey(os.Getenv("BIRD_REALTIME_MASTER_KEY")),
)
if err != nil {
	log.Fatal(err)
}

_, err = client.Realtime.Publish(context.Background(), "rap_01krdgeqcxet5s7t44vh8rt9mg", bird.RealtimePublishParams{
	Event:    "order.updated",
	Channels: []string{"private-encrypted-orders"},
	Data:     map[string]any{"order_id": "ord_123", "status": "shipped"},
})
```

```php
$bird = new Bird(getenv('BIRD_API_KEY'), realtime: new RealtimeOptions(
    key: getenv('BIRD_REALTIME_KEY'),
    secret: getenv('BIRD_REALTIME_SECRET'),
    encryptionMasterKey: getenv('BIRD_REALTIME_MASTER_KEY'),
));

$bird->realtime->publish('rap_01krdgeqcxet5s7t44vh8rt9mg', (new RealtimePublish())
    ->setEvent('order.updated')
    ->setChannels(['private-encrypted-orders'])
    ->setData(['order_id' => 'ord_123', 'status' => 'shipped']));
```

Un canal chiffré doit être le seul canal dans une publication unique. Chaque canal chiffré dérive une clé différente, les autres canaux ne pourraient donc pas déchiffrer la même charge utile scellée. Les SDK rejettent cette diffusion localement, et le API renvoie `E23000` s'il en reçoit une. Pour publier vers plusieurs canaux chiffrés, utilisez un lot avec un canal par événement.

## Renvoyer le secret partagé depuis votre endpoint d'authentification

Votre endpoint d'authentification approuve les abonnements chiffrés de la même manière que les abonnements privés. Utilisez le helper `authorizeChannel` du SDK et la réponse obtient automatiquement le `shared_secret` chaque fois que le nom du canal porte le préfixe chiffré :

```typescript
app.post("/bird/auth", async (req, res) => {
  const { connection_id, channel_name } = req.body;

  const user = getUserFromSession(req);
  if (!user || !mayJoin(user, channel_name)) return res.sendStatus(403);

  res.json(
    await bird.realtime.authorizeChannel({
      connectionId: connection_id,
      channelName: channel_name,
    }),
  );
});
```

```python
@app.post("/bird/auth")
def bird_auth():
    body = request.get_json()
    user = get_user_from_session()
    if user is None or not may_join(user, body["channel_name"]):
        abort(403)

    return client.realtime.authorize_channel(
        connection_id=body["connection_id"],
        channel_name=body["channel_name"],
    )
```

```go
func birdAuth(w http.ResponseWriter, r *http.Request) {
	var body struct {
		ConnectionID string `json:"connection_id"`
		ChannelName  string `json:"channel_name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}

	user, ok := userFromSession(r)
	if !ok || !mayJoin(user, body.ChannelName) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

	auth, err := client.Realtime.AuthorizeChannel(bird.RealtimeChannelAuthorizationParams{
		ConnectionID: body.ConnectionID,
		ChannelName:  body.ChannelName,
	})
	if err != nil {
		http.Error(w, "authorization failed", http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(auth)
}
```

```php
function birdAuth(string $connectionId, string $channelName, User $user): array
{
    if (!mayJoin($user, $channelName)) {
        http_response_code(403);
        exit;
    }

    return $bird->realtime->authorizeChannel($connectionId, $channelName);
}
```

Le SDK dérive un `shared_secret` distinct pour chaque canal. L'autorisation pour `private-encrypted-orders` ne permet donc pas de déchiffrer `private-encrypted-invoices`. Le secret transite dans votre réponse d'autorisation et n'est pas inclus dans la trame d'abonnement envoyée au edge.

## S'abonner et déchiffrer dans le navigateur

Le chiffrement utilise le point d'entrée `@messagebird/realtime/encrypted` distinct. Importez-le et passez-le comme option `encryption` du client :

```typescript
import { BirdRealtime } from "@messagebird/realtime";
import { encryption } from "@messagebird/realtime/encrypted";

const bird = new BirdRealtime({
  appKey: "your-app-key",
  region: "us1",
  authEndpoint: "/bird/auth",
  encryption,
});

const orders = bird.subscribe("private-encrypted-orders");
orders.bind("order.updated", (data) => {
  console.log(data); // decrypted: { order_id: "ord_123", status: "shipped" }
});
```

Les bindings reçoivent du texte en clair. S'abonner sans l'option `encryption` lève immédiatement une erreur, et une réponse d'autorisation sans `shared_secret` fait échouer l'abonnement.

Seul le client navigateur reçoit actuellement les canaux chiffrés. Les clients Swift et Kotlin rejettent les abonnements `private-encrypted-` car ils n'implémentent pas le déchiffrement.

## Rotation de la clé maître

Déployez la nouvelle clé sur chaque éditeur et endpoint d'autorisation en même temps. Pendant la rotation :

1. Les nouvelles publications scellent sous la nouvelle clé.
2. Un client navigateur abonné qui ne peut pas déchiffrer un événement se ré-autorise une fois et récupère le nouveau `shared_secret`.
3. Des instances utilisant des clés maîtres différentes peuvent brièvement publier des événements que certains clients ne peuvent pas déchiffrer : coordonnez le déploiement entre les instances.

Effectuez la rotation d'une clé divulguée ou perdue. La rotation protège les charges utiles futures mais ne peut pas re-sceller les événements précédents ni révoquer les copies de l'ancienne clé.

## Ce que les canaux chiffrés ne font pas

- **Les clients officiels ne prennent pas en charge les événements client.** Le navigateur `trigger()` lève une erreur sur les canaux chiffrés car le client ne scelle pas les charges utiles client-à-client. N'envoyez pas d'événements client en clair depuis un client personnalisé.
- **Présence et chiffrement ne peuvent pas être combinés.** Le préfixe `presence-encrypted-` n'est pas pris en charge. Cache et chiffrement fonctionnent ensemble : les canaux `private-encrypted-cache-` stockent l'événement mis en cache sous forme scellée, mais après une rotation de clé, la copie en cache reste scellée sous l'ancienne clé jusqu'à ce qu'une nouvelle publication la remplace.
- **Les noms de canaux et les noms d'événements ne sont pas chiffrés.** Seule la charge utile l'est. Choisissez des noms de canaux qui ne révèlent pas ce que vous protégez.
- **Le edge Realtime ne peut pas inspecter les charges utiles.** Les noms de canaux et d'événements restent visibles, tandis que la charge utile reste chiffrée.

## Étapes suivantes

- [Autoriser les canaux](/docs/guides/realtime/authorizing-channels) décrit le mécanisme de signature sur lequel ce guide s'appuie.
- [Publier des événements](/docs/guides/realtime/publishing-events) couvre les API de publication et de lot.
- [Canaux cache](/docs/guides/realtime/cache-channels) explique la relecture du dernier événement avec laquelle `private-encrypted-cache-` se combine.

## Related resources

- [Realtime](/products/realtime) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first realtime event](/docs/get-started/send-your-first-realtime-event) (docs)

[Get an implementation brief](/learn/workspace?topic=realtime)
