Cryptographie Symétrique : Principes et Implémentations en Python

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é.

Étiquettes: cryptographie Sécurité Python AES des

Publié le 29 juillet à 01h42