Créa-blog

#100JoursPourCoder
Projet Créa-code

Ressources pour développeur web

Théme de la semaine : Page Speed Insight

Chiffrer / déchiffrer un message en JavaScript (AES, Base64)

Temps de lecture estimé : 9 minutes
Accueil Javascript Chiffrer / déchiffrer un message en JavaScript (AES, Base64)

Sur le web d’aujourd’hui, la sécurité n’est plus une option. Chaque jour, des millions de données circulent entre les navigateurs et les serveurs : mots de passe, messages privés, informations personnelles, voire données bancaires. Ces données doivent impérativement être protégées pour éviter toute fuite ou manipulation malveillante. C’est ici que le chiffrement entre en jeu.

Mais avant de plonger dans le code, il est essentiel de bien comprendre ce que signifie réellement chiffrer un message. Beaucoup de développeurs débutants confondent encore chiffrementcryptagehachage et encodage, alors qu’ils répondent à des besoins très différents.

Dans ce chapitre sur la Cryptographie, nous allons apprendre pas à pas à chiffrer et déchiffrer un message en JavaScript, à l’aide de méthodes concrètes comme Base64 et AES, en nous appuyant sur la Web Crypto API, une interface native du navigateur spécialement conçue pour les opérations cryptographiques.

Vous découvrirez non seulement comment écrire le code, mais aussi pourquoi chaque étape est importante. L’objectif est que vous puissiez, à la fin de cette lecture, comprendre et mettre en œuvre un véritable système de chiffrement sécurisé dans vos propres projets web.

Comprendre le principe du chiffrement

Avant de manipuler la moindre ligne de code, prenons un instant pour comprendre le concept fondamental du chiffrement.

Le chiffrement est une méthode mathématique qui transforme un texte lisible (appelé texte en clair) en un texte illisible (appelé texte chiffré). Seule une personne possédant la bonne clé peut le déchiffrer et retrouver le message original.

Chiffrement symétrique et asymétrique

Il existe deux grandes familles de chiffrement :

  • Le chiffrement symétrique, où la même clé sert à chiffrer et à déchiffrer le message.
    → Exemple : l’algorithme AES (Advanced Encryption Standard).
  • Le chiffrement asymétrique, où une clé publique chiffre le message et une clé privée le déchiffre.
    → Exemple : RSA (utilisé notamment pour les certificats HTTPS).

Dans le cadre d’un projet JavaScript côté client, on utilise souvent le chiffrement symétrique, car il est plus rapide et plus simple à mettre en œuvre.

Pour aller plus loin : Cryptographie symétrique vs asymétrique, type de chiffrement

Les clés, le sel et le vecteur d’initialisation

Lorsqu’un message est chiffré, il faut toujours associer une clé secrète. Cette clé peut être une chaîne de caractères, un mot de passe, ou une donnée générée aléatoirement.

Pour renforcer la sécurité, on utilise souvent un vecteur d’initialisation (IV), qui empêche que deux messages identiques produisent le même résultat chiffré.

Enfin, un sel (salt) peut être ajouté avant le hachage d’un mot de passe ou la dérivation d’une clé, pour rendre impossible la reconnaissance de modèles identiques dans les données.

Pourquoi utiliser JavaScript pour le chiffrement ?

Le langage JavaScript, souvent considéré comme un simple outil pour manipuler le DOM, est en réalité capable de réaliser des opérations cryptographiques complexes grâce à la Web Crypto API. Cette API est aujourd’hui standardisée et disponible dans tous les navigateurs modernes.

Cependant, il faut garder en tête une limite importante : tout ce qui est exécuté côté client peut potentiellement être lu par l’utilisateur. Autrement dit, JavaScript est très pratique pour chiffrer localement, mais il ne doit jamais être votre seule ligne de défense.

Encodage et Base64 : la première étape

Avant de parler de chiffrement fort comme AES, il faut comprendre la différence entre encodage et chiffrement.

Qu’est-ce que l’encodage ?

L’encodage est un processus qui transforme les données d’un format à un autre, sans les protéger. Son but n’est pas de cacher un message, mais simplement de le rendre compatible avec un système ou un protocole.

Un encodage très connu sur le web est le Base64, qui permet de convertir du texte ou des données binaires en caractères ASCII. Cela est très pratique, par exemple, pour stocker des images dans un fichier JSON ou envoyer des données via une requête HTTP.

Exemple d’encodage et de décodage Base64

Voici un exemple simple d’utilisation de Base64 en JavaScript.

// Exemple d'encodage
const message = "Bonjour Créa-Troyes !";
const messageEncode = btoa(message);
console.log("Message encodé :", messageEncode);

// Exemple de décodage
const messageDecode = atob(messageEncode);
console.log("Message décodé :", messageDecode);

Résultat :

Message encodé : Qm9uam91ciBDcsOpYS1Ucm95ZXMgIQ==
Message décodé : Bonjour Créa-Troyes !

Comme vous pouvez le constater, le texte original a été transformé en une suite de caractères illisibles. Cependant, il suffit d’une seule fonction (atob) pour le retrouver. C’est pourquoi Base64 n’est pas un chiffrement, mais simplement un encodage.

Pourquoi utiliser Base64 ?

Base64 est souvent utilisé pour préparer des données avant un vrai chiffrement ou pour transporter des informations binaires dans des environnements qui n’acceptent que du texte.

Par exemple, avant de chiffrer un message avec AES, vous pouvez l’encoder en Base64 afin de le manipuler plus facilement dans une chaîne de caractères.

Attention : Base64 ne protège rien

De nombreux débutants pensent que Base64 permet de cacher des mots de passe ou des jetons d’authentification. En réalité, il suffit de coller une chaîne Base64 dans n’importe quel décodeur en ligne pour retrouver le message original.

Ainsi, le Base64 peut être comparé à une enveloppe transparente : elle facilite le transport, mais ne protège pas le contenu.

Chiffrer avec AES : sécurité et performances

Le véritable chiffrement, celui qui protège vos données, repose sur des algorithmes puissants comme AES.

Qu’est-ce que AES ?

AES, pour Advanced Encryption Standard, est un algorithme de chiffrement symétrique reconnu dans le monde entier. Il est utilisé dans les systèmes bancaires, les réseaux Wi-Fi sécurisés (WPA2, WPA3), et même dans les applications gouvernementales.

Il fonctionne sur des blocs de 128 bits et peut utiliser des clés de 128, 192 ou 256 bits. Plus la clé est longue, plus le chiffrement est sûr — mais aussi légèrement plus lent à exécuter.

La Web Crypto API : l’outil moderne pour chiffrer en JavaScript

Depuis quelques années, la Web Crypto API est devenue la norme pour effectuer des opérations de chiffrement, de hachage et de génération de clés côté navigateur. Elle est accessible via l’objet global window.crypto.subtle.

Voici comment l’utiliser pas à pas.

Générer une clé AES

Pour chiffrer un message, il faut d’abord générer une clé AES.

async function genererCleAES() {
  const cle = await crypto.subtle.generateKey(
    {
      name: "AES-GCM", // Mode de chiffrement
      length: 256      // Taille de la clé
    },
    true,              // Clé exportable ?
    ["encrypt", "decrypt"]
  );
  return cle;
}

Ici, nous utilisons le mode AES-GCM (Galois/Counter Mode), qui offre un excellent niveau de sécurité et une authentification intégrée des données.

Chiffrer un message

Maintenant que nous avons une clé, chiffrons un message.

async function chiffrerMessage(texte, cle) {
  const enc = new TextEncoder();
  const donnees = enc.encode(texte);
  const iv = crypto.getRandomValues(new Uint8Array(12)); // IV aléatoire

  const chiffre = await crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    cle,
    donnees
  );

  return { iv, chiffre };
}

// Exemple d'utilisation :
(async () => {
  const cle = await genererCleAES();
  const message = "Bonjour, ceci est un message secret !";
  const resultat = await chiffrerMessage(message, cle);
  console.log("Texte chiffré :", new Uint8Array(resultat.chiffre));
})();

Chaque exécution produira un texte chiffré différent grâce au vecteur d’initialisation généré aléatoirement. Cela empêche quiconque d’identifier un modèle dans les données.

Déchiffrer le message

Pour retrouver le texte original, il suffit de réutiliser la même clé et le même IV.

async function dechiffrerMessage(cle, iv, donneesChiffrees) {
  const decrypte = await crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    cle,
    donneesChiffrees
  );

  const dec = new TextDecoder();
  return dec.decode(decrypte);
}

// Exemple :
(async () => {
  const cle = await genererCleAES();
  const message = "Créa-Troyes vous apprend à chiffrer en JS !";
  const { iv, chiffre } = await chiffrerMessage(message, cle);
  const texteOriginal = await dechiffrerMessage(cle, iv, chiffre);
  console.log("Message déchiffré :", texteOriginal);
})();

Résultat dans la console :

Message déchiffré : Créa-Troyes vous apprend à chiffrer en JS !

Pourquoi AES-GCM est un excellent choix

Le mode GCM d’AES présente plusieurs avantages :

  • Il combine chiffrement et authentification (vérifie l’intégrité du message).
  • Il est rapide et compatible avec tous les navigateurs récents.
  • Il ne nécessite qu’un petit IV (12 octets) pour fonctionner.

C’est pourquoi il est souvent utilisé dans les applications web modernes.

AES en pratique : l’importance du vecteur IV

L’IV (Initialization Vector) est un élément crucial du chiffrement AES. Il ne doit jamais être réutilisé avec la même clé, sous peine de rendre le chiffrement vulnérable.

Cependant, il n’a pas besoin d’être secret. Vous pouvez le transmettre avec le message chiffré, par exemple sous la forme :

Formation web et informatique - Alban Guillier - Formateur

Des formations informatique pour tous !

Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…

Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.

Découvrez mes formations Qui suis-je ?
const paquet = {
  iv: Array.from(iv),
  data: Array.from(new Uint8Array(chiffre))
};

Puis, lors du déchiffrement, il suffira de reconstruire le Uint8Array à partir des données reçues.

AES et Base64 : un duo pratique

Dans la pratique, il est souvent utile de convertir les données chiffrées (binaires) en Base64, afin de les afficher ou les envoyer facilement dans une requête HTTP.

function bufferToBase64(buffer) {
  let binary = '';
  const bytes = new Uint8Array(buffer);
  bytes.forEach(b => binary += String.fromCharCode(b));
  return btoa(binary);
}

Ainsi, le texte chiffré devient une chaîne de caractères manipulable, tout en restant incompréhensible sans la clé.

Sécuriser les clés et les données

Nous savons maintenant chiffrer et déchiffrer un message en JavaScript à l’aide d’AES, mais il reste un point crucial : la gestion des clés. En matière de sécurité, la clé est littéralement le cœur du système. Si elle est compromise, le chiffrement ne sert plus à rien.

Ne jamais stocker une clé dans le code

C’est une erreur fréquente chez les débutants : écrire directement la clé dans le script JavaScript, par exemple :

const cle = "maCleSecrete123";

Cette pratique est à proscrire absolument, car n’importe qui peut consulter le code source du navigateur. Une clé doit être générée dynamiquementstockée de manière sécurisée, ou dérivée d’un mot de passe utilisateur.

Utiliser un mot de passe pour générer une clé

Plutôt que de stocker une clé en clair, il est plus sûr de la dériver à partir d’un mot de passe saisi par l’utilisateur. C’est là qu’interviennent des fonctions de dérivation de clé comme PBKDF2 (Password-Based Key Derivation Function 2).

Voici un exemple d’implémentation avec la Web Crypto API :

async function deriveKeyFromPassword(password, salt) {
  const enc = new TextEncoder();
  const baseKey = await crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    "PBKDF2",
    false,
    ["deriveKey"]
  );

  const derivedKey = await crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 100000,
      hash: "SHA-256"
    },
    baseKey,
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );

  return derivedKey;
}

Dans cet exemple :

  • Le sel (salt) est une donnée aléatoire ajoutée au mot de passe avant le hachage.
  • Le nombre d’itérations rend le calcul plus long, donc plus difficile à attaquer.
  • Le résultat final est une clé AES dérivée à partir du mot de passe.

Ainsi, même si deux utilisateurs choisissent le même mot de passe, leurs clés finales seront différentes grâce au sel.

Générer et stocker un sel sécurisé

Le sel doit être aléatoire et unique pour chaque utilisateur ou session.

const salt = crypto.getRandomValues(new Uint8Array(16));

Il peut être stocké localement (par exemple dans localStorage) ou envoyé au serveur, car il n’a pas besoin d’être secret.
En revanche, il ne faut jamais réutiliser le même couple mot de passe + sel, sinon le chiffrement perd en efficacité.

Sécuriser le stockage local

Si vous devez conserver temporairement des données chiffrées dans le navigateur, préférez l’API IndexedDB ou localStorage en combinaison avec un chiffrement fort.

Par exemple, avant d’enregistrer une note ou un message sensible côté client, vous pouvez :

  1. Dériver une clé à partir du mot de passe utilisateur.
  2. Chiffrer le contenu avec AES-GCM.
  3. Enregistrer le texte chiffré en Base64 dans localStorage.

Cette approche permet d’éviter que des données confidentielles soient stockées en clair.

Importer ou exporter une clé

Il est parfois nécessaire d’exporter une clé pour la sauvegarder ou la transmettre sous forme de chaîne Base64.

async function exportKeyToBase64(key) {
  const exported = await crypto.subtle.exportKey("raw", key);
  return bufferToBase64(exported);
}

À l’inverse, pour réutiliser une clé sauvegardée :

async function importKeyFromBase64(base64) {
  const binary = atob(base64);
  const bytes = new Uint8Array([...binary].map(c => c.charCodeAt(0)));
  return crypto.subtle.importKey("raw", bytes, "AES-GCM", true, ["encrypt", "decrypt"]);
}

Grâce à ces fonctions, vous pouvez restaurer une clé entre plusieurs sessions ou navigateurs, tout en gardant un haut niveau de sécurité.

Exemple concret : créer une mini application web

Maintenant que les fondations sont solides, voyons comment mettre tout cela en pratique. Nous allons construire une mini application web capable de chiffrer et déchiffrer un message à partir d’un mot de passe saisi par l’utilisateur.

Le code HTML

Créons d’abord une page simple :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Chiffrement AES en JavaScript</title>
  <style>
    body { font-family: sans-serif; max-width: 600px; margin: auto; padding: 20px; }
    textarea, input { width: 100%; margin-top: 10px; }
    button { margin-top: 15px; padding: 10px; }
  </style>
</head>
<body>
  <h1>Chiffrez votre message</h1>
  <textarea id="message" rows="4" placeholder="Votre message..."></textarea>
  <input type="password" id="motdepasse" placeholder="Votre mot de passe">
  <button id="btnChiffrer">Chiffrer</button>
  <button id="btnDechiffrer">Déchiffrer</button>
  <textarea id="resultat" rows="4" placeholder="Résultat..."></textarea>
  <script src="app.js"></script>
</body>
</html>

Cette interface minimaliste permettra de saisir un message, un mot de passe, puis d’afficher le résultat chiffré ou déchiffré.

2. Le script JavaScript

Voici le contenu du fichier app.js :

const messageInput = document.getElementById('message');
const motdepasseInput = document.getElementById('motdepasse');
const resultat = document.getElementById('resultat');

document.getElementById('btnChiffrer').onclick = async () => {
  const texte = messageInput.value;
  const motdepasse = motdepasseInput.value;
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const cle = await deriveKeyFromPassword(motdepasse, salt);

  const iv = crypto.getRandomValues(new Uint8Array(12));
  const enc = new TextEncoder();
  const donnees = enc.encode(texte);

  const chiffre = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, cle, donnees);

  const package = {
    salt: bufferToBase64(salt),
    iv: bufferToBase64(iv),
    data: bufferToBase64(chiffre)
  };

  resultat.value = JSON.stringify(package);
};

document.getElementById('btnDechiffrer').onclick = async () => {
  try {
    const motdepasse = motdepasseInput.value;
    const obj = JSON.parse(resultat.value);

    const salt = base64ToBuffer(obj.salt);
    const iv = base64ToBuffer(obj.iv);
    const data = base64ToBuffer(obj.data);

    const cle = await deriveKeyFromPassword(motdepasse, salt);
    const decrypte = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, cle, data);

    const dec = new TextDecoder();
    messageInput.value = dec.decode(decrypte);
  } catch (e) {
    alert("Erreur : mot de passe incorrect ou données invalides.");
  }
};

// Fonctions utilitaires
function bufferToBase64(buffer) {
  let binary = '';
  const bytes = new Uint8Array(buffer);
  bytes.forEach(b => binary += String.fromCharCode(b));
  return btoa(binary);
}

function base64ToBuffer(base64) {
  const binary = atob(base64);
  return new Uint8Array([...binary].map(c => c.charCodeAt(0)));
}

Ce petit programme :

  • Dérive une clé AES à partir du mot de passe.
  • Chiffre le message avec AES-GCM.
  • Encode le tout en Base64 pour l’affichage.
  • Permet ensuite de déchiffrer le message grâce au même mot de passe.

Essayer l’application

En ouvrant votre page dans un navigateur, vous pouvez saisir :

  • Un message : « Les formations de Créa-Troyes sont gratuites ! »
  • Un mot de passe : « sécurité123 »

Cliquez sur Chiffrer, puis copiez le texte obtenu (il ressemble à un long JSON avec du Base64). En cliquant ensuite sur Déchiffrer, le message original réapparaît instantanément.

Cela illustre parfaitement le fonctionnement d’un chiffrement symétrique sécurisé dans un contexte web.

Bonnes pratiques et erreurs à éviter

Pour clore la partie technique, voyons quelques conseils essentiels à garder en tête.

Ne pas confondre encodage, hachage et chiffrement

  • L’encodage (Base64, URL, UTF-8) ne protège rien.
  • Le hachage (SHA-256, bcrypt) transforme un mot de passe de façon irréversible.
  • Le chiffrement (AES, RSA) permet de cacher et de retrouver des données.

Ces trois techniques répondent à des besoins différents.

Toujours utiliser un IV unique

Le vecteur d’initialisation doit être généré aléatoirement pour chaque chiffrement.
Réutiliser le même IV avec la même clé expose les messages à des attaques statistiques.

Ne jamais transmettre une clé en clair

Si vous devez échanger une clé entre un client et un serveur, chiffrez-la avec une clé publique (RSA) ou établissez un canal sécurisé HTTPS. C’est ce qu’on appelle le transport sécurisé de clé.

Vérifier la compatibilité des navigateurs

La Web Crypto API est largement supportée par les navigateurs modernes, mais certaines fonctions (comme deriveKey) peuvent varier. Il est recommandé de consulter la documentation officielle de MDN Web Docs pour s’assurer de la compatibilité avant mise en production.

Toujours gérer les erreurs

Le chiffrement et le déchiffrement peuvent échouer, notamment si le mot de passe ou l’IV ne correspondent pas. Il faut donc prévoir des messages d’erreur clairs pour informer l’utilisateur sans révéler trop d’informations techniques.


Vous savez désormais comment chiffrer et déchiffrer un message en JavaScript à l’aide des outils modernes que sont Base64 et AES-GCM. Mais surtout, vous comprenez pourquoi chaque étape est essentielle pour assurer une véritable sécurité des données côté client.

Ce savoir n’est pas réservé aux experts en cybersécurité : il fait désormais partie du bagage de tout développeur web responsable. Que ce soit pour protéger des notes, des messages, des identifiants ou des données locales, le chiffrement est une compétence incontournable.

Les techniques abordées dans cet article ne servent pas seulement à coder des démonstrations ; elles ouvrent la voie à des applications concrètes comme :

  • Des messageries chiffrées directement dans le navigateur.
  • Des systèmes de stockage local sécurisé pour les PWA.
  • Des formulaires qui protègent les informations avant même leur envoi au serveur.

Le web moderne ne se limite plus à afficher du contenu : il devient un environnement où la confidentialité et la protection des données sont des exigences fondamentales.

Alors, prenez le temps d’expérimenter, testez les différentes méthodes présentées ici, et adaptez-les à vos projets. Car dans un monde où la donnée est le nouvel or, savoir la protéger fait de vous bien plus qu’un développeur : un véritable architecte de la confiance numérique.

Chapitre 7. Empreinte SHA 256