À propos de ce tutoriel
Dans ce tutoriel je vous propose de découvrir l'API Web Crypto qui vous permet l'accès aux fonctionnalité cryptographiques côté client.
Génération de la clef
La première étape pour chiffrer et déchiffrer les fichiers est de générer une clef. On n'utilisera pas directement la clef envoyé par l'utilisateur mais on va dériver une clef. Cela permet d'éviter de révéler le mot de passe de l'utilisateur si le système venait à être cassé.
const encoder = new TextEncoder();
const algo = {
name: "AES-GCM",
length: 256,
};
async function getEncryptionKey(value, salt) {
const masterPassword = await crypto.subtle.importKey(
"raw",
encoder.encode(value),
{ name: "PBKDF2" },
false,
["deriveKey"]
);
return await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: encoder.encode(salt),
iterations: 1000,
hash: { name: "SHA-1" },
},
masterPassword,
algo,
false,
["encrypt", "decrypt"]
);
}
Chiffrer le fichier
Une fois la clef obtenue on peut l'utiliser pour chiffrer le fichier.
const file = inputFile.files[0];
if (!file) {
alert("Vous devez sélectionner un fichier");
return;
}
const encryptedFile = await crypto.subtle.encrypt(
{ ...algo, iv: encoder.encode(file.name) },
ENCRYPTION_KEY,
await file.arrayBuffer()
);
const formData = new FormData();
formData.append("file", new Blob([encryptedFile]), file.name);
await fetch("/upload.php", {
method: "post",
body: formData,
});
form.reset();
Déchiffrer le fichier
Lors du clic sur un lien on récupère le fichier depuis le serveur et on le decode à la volée pour le faire télécharger à l'utilisateur.
/**
* @param {ArrayBuffer} file
* @param {string} filename
*/
function downloadFile (file, filename) {
const a = document.createElement('a')
a.href = window.URL.createObjectURL(new Blob([file]))
a.download = filename
a.click()
}
document.querySelectorAll(".list-group a").forEach((a) => {
a.addEventListener("click", async (e) => {
e.preventDefault();
const href = a.getAttribute("href");
const hrefParts = href.split("/");
const filename = hrefParts[hrefParts.length - 1];
const response = await fetch(href);
const fileData = await response.arrayBuffer();
const file = await crypto.subtle.decrypt(
{ ...algo, iv: encoder.encode(filename) },
await getEncryptionKey(),
fileData
);
downloadFile(file, filename);
});
});