🔒

Sécuriser une Inbox avec chiffrement de bout en bout (sealed-box X25519)

Activez le mode E2E sur votre drop-box Coffrify : générez une paire de clés X25519 dans le navigateur, stockez la clé privée enveloppée (PBKDF2 + AES-256-GCM), puis soumettez chaque dépôt avec un `encryption_mode: "e2e_v1_asym"` pour que Coffrify ne voie jamais le contenu en clair.

Télécharger en PDF

Le mode E2E v1 asymétrique de Coffrify Inbox repose sur une paire de clés X25519 générée entièrement dans le navigateur du propriétaire de la drop-box. La clé publique est stockée en clair sur Coffrify pour que les expéditeurs puissent chiffrer leur envoi via un sealed-box libsodium. La clé privée, elle, ne quitte jamais le poste du propriétaire : elle est enveloppée avec PBKDF2-SHA256 (600 000 itérations minimum) et AES-256-GCM avant d'être stockée côté serveur. Coffrify ne voit donc jamais le contenu des fichiers, ni la clé symétrique qui les protège. Ce guide couvre trois opérations : activer l'E2E sur une drop-box, soumettre un dépôt chiffré via l'API publique, et désactiver temporairement le mode E2E sans perdre la capacité de déchiffrer les anciens dépôts.

Endpoints concernés

POST/api/workspace/inbox/e2eActivation du chiffrement E2E sur la drop-box active : dépose la clé publique X25519 et l'enveloppe de clé privée. Réservé aux rôles `owner` et `admin`.
GET/api/workspace/inbox/e2eRécupère l'état E2E de la drop-box active : `enabled`, `public_key`, `private_key_wrapped` et `activated_at`. Utilisé par le navigateur du propriétaire pour déchiffrer les soumissions.
DELETE/api/workspace/inbox/e2eDésactive le mode E2E (passe `e2e_enabled` à `false`) sans supprimer le keypair. Les soumissions antérieures restent déchiffrables hors ligne par le propriétaire.
POST/api/public/inbox/submitSoumission publique d'un dépôt. Lorsque la drop-box a `e2e_enabled = true`, ce endpoint exige `encryption_mode: "e2e_v1_asym"` et `encryption_metadata` contenant la clé symétrique enveloppée. Authentifié par captcha (header `x-captcha-token`), pas par token API.

Authentification

Les routes /api/workspace/inbox/e2e (GET, POST, DELETE) requièrent une session propriétaire ou administrateur du workspace. Aucun token API n'est accepté sur ces routes : elles utilisent la session de l'utilisateur connecté. Le rôle doit être owner ou admin ; un rôle member reçoit une erreur 403 forbidden. La route de soumission publique /api/public/inbox/submit n'utilise pas de token API mais exige un header x-captcha-token valide avec le scope drop-box. Si vous créez un token API pour des intégrations serveur (format cit_live_…), utilisez plutôt la route /api/v1/inbox/submit avec un scope read_write.

Corps de la requête, Activation E2E (POST /api/workspace/inbox/e2e)

ChampTypeRequisDescription
public_keystring (base64url)OuiClé publique X25519 de 32 octets encodée en base64url. Doit décoder exactement en 32 octets.
private_key_wrappedobjectOuiEnveloppe de la clé privée (voir sous-champs ci-dessous). Ne doit jamais contenir de champ private_key, raw_key, plaintext ou passphrase.
private_key_wrapped.algorithmstringOuiDoit valoir exactement "x25519-sealed-box-v1".
private_key_wrapped.kdfstringOuiDoit valoir exactement "pbkdf2-sha256".
private_key_wrapped.iterationsnumberOuiNombre d'itérations PBKDF2. Minimum : 600 000 (plancher OWASP 2023).
private_key_wrapped.saltstringOuiSel PBKDF2 encodé (base64 ou hex) généré côté client.
private_key_wrapped.ivstringOuiIV AES-256-GCM encodé (base64 ou hex).
private_key_wrapped.ciphertextstringOuiClé privée chiffrée par AES-256-GCM avec la clé dérivée du mot de passe propriétaire.

Corps de la requête, Soumission E2E (POST /api/public/inbox/submit)

ChampTypeRequisDescription
workspace_slugstringOui*Slug du workspace cible. Alternatif : workspace_domain.
workspace_domainstringOui*Domaine personnalisé vérifié. Alternatif : workspace_slug.
filesarrayOuiTableau d'objets { name: string, size: number, display_type?: string }. Au moins 1 fichier, max 50 (ou limite de la box).
requester_emailstringOuiAdresse e-mail de l'expéditeur. Max 254 caractères, format valide.
requester_namestringConditionnelNom de l'expéditeur. Requis si la box a require_requester_name = true.
messagestringNonMessage accompagnant le dépôt. Tronqué à message_max_chars de la box (défaut 500).
encryption_modestringOui (E2E)Doit valoir "e2e_v1_asym" si la box a e2e_enabled = true. Sinon "server_side" ou omis.
encryption_metadataobjectOui (E2E)Métadonnées de chiffrement. Doit contenir wrapped_symmetric_key (string, sealed-box libsodium) et files (object avec IV par fichier).
custom_field_valuesobjectConditionnelRéponses aux champs personnalisés de la box, clé = UUID du champ.

Exemples d'appels

# Étape 1, Activer E2E sur la drop-box active (session cookie requise)
curl -X POST https://app.coffrify.com/api/workspace/inbox/e2e \
-H "Content-Type: application/json" \
-b "session=<votre_cookie_de_session>" \
-d '{
"public_key": "mYpUBlIcKeYbAsE64UrL32BYtEs=AAAA",
"private_key_wrapped": {
"algorithm": "x25519-sealed-box-v1",
"kdf": "pbkdf2-sha256",
"iterations": 600000,
"salt": "c2FsdC1iYXNlNjQtZXhhbXBsZQ==",
"iv": "aXYtYmFzZTY0LWV4YQ==",
"ciphertext": "Y2lwaGVydGV4dC1iYXNlNjQ="
}
}'
 
# Étape 2, Soumettre un dépôt E2E (captcha requis)
curl -X POST https://app.coffrify.com/api/public/inbox/submit \
-H "Content-Type: application/json" \
-H "x-captcha-token: <votre_token_captcha>" \
-d '{
"workspace_slug": "acme",
"requester_email": "alice@exemple.fr",
"requester_name": "Alice Dupont",
"message": "Documents confidentiels chiffrés côté client.",
"encryption_mode": "e2e_v1_asym",
"encryption_metadata": {
"wrapped_symmetric_key": "<sealed_box_output_base64>",
"files": {
"0": { "iv": "<iv_fichier_0_base64>" }
}
},
"files": [
{ "name": "contrat.pdf.enc", "size": 204800 }
]
}'

Réponses

POST /api/workspace/inbox/e2e (activation réussie) : retourne 200 OK.

{ "ok": true }

GET /api/workspace/inbox/e2e : retourne l'état courant du keypair.

{
"enabled": true,
"public_key": "mYpUBlIcKeYbAsE64UrL32BYtEsAAAAAAAAAAAAAA",
"private_key_wrapped": {
"algorithm": "x25519-sealed-box-v1",
"kdf": "pbkdf2-sha256",
"iterations": 600000,
"salt": "c2FsdC1iYXNlNjQtZXhhbXBsZQ==",
"iv": "aXYtYmFzZTY0LWV4YQ==",
"ciphertext": "Y2lwaGVydGV4dC1iYXNlNjQ="
},
"activated_at": "2026-06-01T14:32:00.000Z"
}

POST /api/public/inbox/submit avec E2E (dépôt créé) : retourne 200 OK avec les URLs presignées pour chaque fichier.

{
"ok": true,
"submission_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"tracking_token": "pQrStUvWxYzAbCdEfGhI",
"tracking_expires_at": "2026-07-06T14:32:00.000Z",
"uploads": [
{
"name": "contrat.pdf.enc",
"url": "https://s3.fr-par.scw.cloud/coffrify-prod/drops/ws-id/sub-id/0-a1b2c3d4-contrat.pdf.enc?X-Amz-Signature=...",
"content_type": "application/octet-stream",
"storage_path": "drops/ws-id/sub-id/0-a1b2c3d4-contrat.pdf.enc",
"required_headers": {
"Content-Type": "application/octet-stream"
}
}
]
}

Erreurs

Code HTTPClé d'erreurQuandRésolution
400public_key invalide (attendu base64url)La clé publique n'est pas en base64url ou ne fait pas 32 octets après décodage.Utilisez la sortie directe de sodium.to_base64(keyPair.publicKey, sodium.base64_variants.URLSAFE_NO_PADDING).
400private_key_wrapped invalideL'objet private_key_wrapped manque un champ obligatoire, l'algorithme n'est pas x25519-sealed-box-v1, ou iterations < 600000.Vérifiez que tous les sous-champs sont présents et que iterations >= 600000.
400private_key_wrapped contient un champ interditLe JSON de l'enveloppe contient private_key, raw_key, plaintext ou passphrase.Ne jamais inclure la clé en clair dans l'enveloppe. Seul le ciphertext est accepté.
400DROPBOX_E2E_REQUIREDLa drop-box a e2e_enabled = true mais la soumission envoyait encryption_mode: "server_side" ou omettait le champ.Passez encryption_mode: "e2e_v1_asym" et fournissez encryption_metadata.
400DROPBOX_E2E_METADATA_INVALIDencryption_metadata est absent, ou wrapped_symmetric_key n'est pas une string, ou files est absent.Vérifiez la structure : { wrapped_symmetric_key: string, files: { "0": { iv: string }, ... } }.
400DROPBOX_E2E_NOT_ENABLEDLa soumission envoyait encryption_mode: "e2e_v1_asym" mais la box n'a pas E2E activé.Activez d'abord E2E via POST /api/workspace/inbox/e2e, ou passez server_side si la box ne le requiert pas.
401CAPTCHA_REQUIREDLe header x-captcha-token est absent ou invalide sur la route publique.Intégrez le composant captcha Coffrify ou obtenez un token via l'API captcha avec scope=drop-box.
401unauthorizedSession expirée ou absente sur les routes workspace.Reconnectez-vous au dashboard Coffrify.
402upload_blockedLe workspace a atteint son quota mensuel de dépôts ou a un paiement en retard (uploads_blocked_until).Mettez à niveau le plan ou régularisez le paiement depuis le dashboard.
403forbiddenL'utilisateur connecté a le rôle member, qui ne peut pas activer/désactiver l'E2E.Utilisez un compte avec le rôle owner ou admin.
404drop_box_not_foundAucune drop-box active n'existe pour le workspace.Créez une drop-box depuis l'Inbox Console avant d'activer E2E.
409DROPBOX_E2E_ACTIVATION_INCOMPLETELa box a e2e_enabled = true mais e2e_public_key est null : l'activation est en cours.Finalisez l'activation E2E depuis l'Inbox Console du propriétaire.
500Échec d'activation E2EErreur base de données lors de l'update du keypair.Réessayez. Si le problème persiste, contactez le support Coffrify.

Voir aussi

  • Créer un token API d'Inbox (`cit_live_…`)
  • Soumettre un dépôt via token API serveur (`cit_live_…`)
  • Configurer des webhooks Inbox (`deposit.created`)
  • Champs personnalisés sur une drop-box
  • Référence complète de l'API Inbox
Continuer

Autres tutoriels à suivre