Introduction à la Cryptographie Symétrique
La cryptographie symétrique, également appelée chiffrement à clé secrète, repose sur l'utilisation d'une unique clé pour les opérations de chiffrement et de déchiffrement. Imaginez devoir envoyer un colis sécurisé : vous utilisez une clé pour verrouiller le colis avant son expédition, et votre destinataire utilise la même clé pour le déverrouiller à réception. Ce principe garantit que seules les parties possédant la clé peuvent accéder au contenu.
Concepts Clés
- Clé unique : L'expéditeur et le destinataire partagent la même clé secrète.
- Algorithmes courants : Parmi les algorithmes symétriques les plus répandus, on trouve AES (Advanced Encryption Standard), DES (Data Encryption Standard) et 3DES (Triple DES).
Implémentation en Python avec AES
Nous allons illustrer l'utilisation du chiffrement symétrique avec l'algorithme AES en Python, en explorant ses concepts fondamentaux.
Comprendre AES
Caractéristiques d'AES
- Taille de clé : AES supporte des clés de 128, 192 ou 256 bits, offrant différents niveaux de sécurité.
- Chiffrement par blocs : AES traite les données par blocs de 128 bits. Si le message d'origine n'est pas un multiple de 128 bits, un processus de remplissage (padding) est appliqué pour compléter le dernier bloc.
- Modes opératoires : AES propose plusieurs modes de chiffrement par blocs, dont les plus courants sont ECB (Eletcronic Codebook) et CBC (Cipher Block Chaining).
- ECB : Chaque bloc de texte clair est chiffré indépendamment. Ce mode est simple mais moins sécurisé car des blocs identiques produisent des blocs chiffrés identiques.
- CBC : Chaque bloc de texte clair est combiné (généralement par XOR) avec le bloc de texte chiffré précédent avant d'être chiffré. Cela améliore la sécurité en rendant les blocs chiffrés dépendants les uns des autres.
- CFB et OFB : Ces modes utilisent des mécanismes de rétroaction pour transformer le chiffrement par blocs en un chiffrement de flux, augmentant la complexité pour les attaquants.
- Vecteur d'initialisation (IV) : À l'exception du mode ECB, la plupart des modes nécessitent un vecteur d'initialisation (IV). L'IV est une donnée aléatoire utilisée pour initialiser le processus de chiffrement, garantissant que des données identiques chiffrées avec la même clé produisent des résultats différents.
Utilisation d'AES en Python
Pour utiliser AES en Python, nous pouvons nous appuyer sur des bibliothèques comme pycryptodome.
Installation
pip install pycryptodome
Exemple simple (Mode ECB)
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
# Clé secrète (doit être de 16, 24 ou 32 octets pour AES)
secret_key = b"MySecretKey12345"
data_to_encrypt = "Ceci est un message secret."
# Création de l'objet de chiffrement AES en mode ECB
cipher = AES.new(secret_key, AES.MODE_ECB)
# Préparation des données : conversion en octets et remplissage
# Le remplissage garantit que la longueur des données est un multiple de 16 octets
padded_data = pad(data_to_encrypt.encode('utf-8'), AES.block_size)
# Chiffrement des données
encrypted_data = cipher.encrypt(padded_data)
print(f"Données chiffrées (bytes): {encrypted_data}")
# --- Déchiffrement ---
# Création de l'objet de déchiffrement (identique à l'objet de chiffrement)
decipher = AES.new(secret_key, AES.MODE_ECB)
# Déchiffrement des données
decrypted_padded_data = decipher.decrypt(encrypted_data)
# Suppression du remplissage
decrypted_data = unpad(decrypted_padded_data, AES.block_size).decode('utf-8')
print(f"Données déchiffrées: {decrypted_data}")
Exemple en Mode CBC
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
# Clé secrète et Vecteur d'Initialisation (IV)
secret_key = b"AnotherSecureKey12" # 16 octets pour AES
iv = b"MyInitVector0123" # 16 octets pour AES
data_to_encrypt = "Message pour le mode CBC."
# Création de l'objet de chiffrement AES en mode CBC
cipher = AES.new(secret_key, AES.MODE_CBC, iv)
# Préparation des données et chiffrement
padded_data = pad(data_to_encrypt.encode('utf-8'), AES.block_size)
encrypted_data = cipher.encrypt(padded_data)
# Encodage en Base64 pour une transmission plus aisée
encrypted_base64 = base64.b64encode(encrypted_data).decode('utf-8')
print(f"Données chiffrées (Base64): {encrypted_base64}")
# --- Déchiffrement ---
# Création de l'objet de déchiffrement
decipher = AES.new(secret_key, AES.MODE_CBC, iv)
# Décodage Base64 des données reçues
received_encrypted_data = base64.b64decode(encrypted_base64)
# Déchiffrement
decrypted_padded_data = decipher.decrypt(received_encrypted_data)
# Suppression du remplissage et décodage
decrypted_data = unpad(decrypted_padded_data, AES.block_size).decode('utf-8')
print(f"Données déchiffrées: {decrypted_data}")
Utilisation avec DES
DES et 3DES sont d'autres algorithmes de chiffrement symétrique. L'utilisation en Python est similaire à celle d'AES, avec des différences notables dans la taille de la clé (8 octets pour DES) et la taille des blocs (64 bits pour DES).
Exemple de chiffrement/déchiffrement DES en mode CBC
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad, unpad
import base64
class SymmetricCrypto:
def __init__(self, key: bytes, mode: int, iv: bytes = None):
self.key = key
self.mode = mode
self.iv = iv
if mode == DES.MODE_ECB:
self.cipher = DES.new(self.key, self.mode)
else:
self.cipher = DES.new(self.key, self.mode, self.iv)
def encrypt(self, data: str) -> bytes:
data_bytes = data.encode('utf-8')
# DES utilise des blocs de 8 octets
padded_data = pad(data_bytes, DES.block_size)
encrypted = self.cipher.encrypt(padded_data)
return encrypted
def decrypt(self, encrypted_data: bytes) -> str:
decrypted_padded = self.cipher.decrypt(encrypted_data)
decrypted = unpad(decrypted_padded, DES.block_size).decode('utf-8')
return decrypted
if __name__ == '__main__':
des_key = b"MyDESKey" # 8 octets pour DES
des_iv = b"DESIV1234" # 8 octets pour DES
# Utilisation en mode CBC
crypto_cbc = SymmetricCrypto(key=des_key, mode=DES.MODE_CBC, iv=des_iv)
original_message = "Message pour DES CBC."
encrypted_cbc = crypto_cbc.encrypt(original_message)
encrypted_cbc_base64 = base64.b64encode(encrypted_cbc).decode()
print(f"DES CBC Chiffré (Base64): {encrypted_cbc_base64}")
# Déchiffrement CBC
received_encrypted_cbc = base64.b64decode(encrypted_cbc_base64)
decrypted_cbc = crypto_cbc.decrypt(received_encrypted_cbc)
print(f"DES CBC Déchiffré: {decrypted_cbc}")
# Utilisation en mode ECB
crypto_ecb = SymmetricCrypto(key=des_key, mode=DES.MODE_ECB)
original_message_ecb = "Message pour DES ECB."
encrypted_ecb = crypto_ecb.encrypt(original_message_ecb)
print(f"DES ECB Chiffré (bytes): {encrypted_ecb}")
# Déchiffrement ECB
decrypted_ecb = crypto_ecb.decrypt(encrypted_ecb)
print(f"DES ECB Déchiffré: {decrypted_ecb}")
Utilisation du Chiffrement Symétrique en Frontend (JavaScript)
Pour le chiffrement côté client, la bibliothèque CryptoJS est couramment utilisée.
Exemple avec CryptoJS (AES CBC)
Notez que CryptoJS attend souvent des données encodées en Base64 pour le déchiffrement.
// Assurez-vous d'avoir inclus la bibliothèque CryptoJS
// npm install crypto-js
// ou via CDN
const key = CryptoJS.enc.Utf8.parse("aVerySecretKey123"); // Clé de 16, 24 ou 32 octets
const iv = CryptoJS.enc.Utf8.parse("myInitVector1234"); // Clé de 16 octets pour AES
// Fonction de chiffrement
function encryptData(data) {
const srcs = CryptoJS.enc.Utf8.parse(data);
const encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7 // Utilisation du padding PKCS7
});
// Retourne la représentation hexadécimale du texte chiffré
return encrypted.ciphertext.toString();
}
// Fonction de déchiffrement
function decryptData(hexString) {
// Conversion de la chaîne hexadécimale en mots CryptoJS
const encryptedHex = CryptoJS.enc.Hex.parse(hexString);
// Conversion des mots en Base64 pour l'API decrypt
const srcs = CryptoJS.enc.Base64.stringify(encryptedHex);
const decrypt = CryptoJS.AES.decrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// Conversion du résultat en chaîne UTF-8
const decryptedString = decrypt.toString(CryptoJS.enc.Utf8);
return decryptedString;
}
// Exemple d'utilisation
const message = "Bonjour le monde!";
const encrypted = encryptData(message);
console.log("Chiffré (Hex):", encrypted); // Devrait afficher une chaîne hexadécimale
const decrypted = decryptData(encrypted);
console.log("Déchiffré:", decrypted); // Devrait afficher "Bonjour le monde!"
Interaction Client-Serveur et Base64
Lors de la communication entre un client et un serveur, il est fréquent de transmettre les données chiffrées sous forme de chaînes encodées en Base64 plutôt qu'en octets bruts. Ceci est dû à la meilleure prise en charge des chaînes de caractères dans les protocoles comme HTTP.
Le codage Base64
Le codage Base64 représente des données binaires dans un format texte ASCII. Il utilise un alphabet de 64 caractères (A-Z, a-z, 0-9, +, /) pour encoder des groupes de 3 octets en 4 caractères. Si la longueur des données n'est pas un multiple de 3, un ou deux caractères '=' sont ajoutés à la fin comme remplissage.
Python et Base64
import base64
original_string = "Données à encoder en Base64."
# Encodage en Base64
encoded_bytes = base64.b64encode(original_string.encode('utf-8'))
encoded_string = encoded_bytes.decode('utf-8')
print(f"Encodé en Base64: {encoded_string}")
# Décodage Base64
decoded_bytes = base64.b64decode(encoded_string)
decoded_string = decoded_bytes.decode('utf-8')
print(f"Décodé depuis Base64: {decoded_string}")
Intégration avec Flask
Voici comment vous pourriez renvoyer des données chiffrées depuis une application Flask.
from flask import Flask, jsonify
import json
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
app = Flask(__name__)
def encrypt_aes_data(data_str: str, key: bytes, mode: int = AES.MODE_ECB, iv: bytes = None) -> bytes:
"""Chiffre des données en utilisant AES."""
if mode == AES.MODE_ECB:
cipher = AES.new(key, mode)
else:
cipher = AES.new(key, mode, iv)
padded_data = pad(data_str.encode('utf-8'), AES.block_size)
encrypted_data = cipher.encrypt(padded_data)
return encrypted_data
@app.route("/secure_data")
def get_secure_data():
original_data = {"message": "Accès sécurisé", "status": "ok"}
data_json = json.dumps(original_data)
# Clé et IV pour le chiffrement (à garder secrets !)
secret_key = b"MyFlaskAppSecureKey" # 16 octets
aes_iv = b"FlaskAppIV12345678" # 16 octets pour CBC
# Chiffrement en mode CBC
encrypted_bytes = encrypt_aes_data(data_json, secret_key, AES.MODE_CBC, aes_iv)
# Encodage en Base64 pour la transmission HTTP
encrypted_base64 = base64.b64encode(encrypted_bytes).decode('utf-8')
# Retourner les données chiffrées en Base64
return jsonify({"encrypted_payload": encrypted_base64})
if __name__ == '__main__':
# Pour le développement uniquement. Ne pas utiliser en production sans configuration appropriée.
app.run(debug=True)
Ce pattern d'encodage des données chiffrées en Base64 avant transmission est également applicable avec d'autres frameworks web comme Django.
Considérations sur la Sécurité
Bien que le chiffrement symétrique offre une couche de protection, il est crucial de noter que la sécurité des données dépend entièrement de la confidentialité de la clé secrète. Si la clé est compromise, le chiffrement devient inutile. Dans les applications web, partager la clé secrète avec le frontend (même si elle est utilisée pour le déchiffrement) peut présenter des risques de sécurité.