Je vous présente ici une méthode de chiffrement personnel d’images avec Python. Pour se faire, nous allons prendre une image quelconque, par exemple celle-ci:
Un chiffrement personnel d’images avec python: préliminaires
Une image est une matrice de triplets ou quadruplets (selon les formats). Pour notre exemple, l’image est au format JPG, donc chaque pixel qui la compose est un triplet (R,B,V).
Il faut donc avant tout récupérer ces triplets à l’aide de Python. Pour cela, nous allons avoir besoin des modules pillow et numpy.
Installation des modules
Si vous avez une distribution standard de Python, vous pouvez les installer à l’aide de “pip”.
pip install pillow numpy
Si vous êtes sous Anaconda, ce sera:
conda install pillow numpy
Récupérations des données de l’images
from PIL import Image import numpy as np # Charger l'image image_path = 'ruban.jpg' image = Image.open(image_path) # Convertir l'image en matrice numpy image_matrix = np.array(image) # Afficher la matrice print(image_matrix)
np.array(image)
va nous donner une matrice. La matrice commence par:
[[[10 16 28]
[ 3 9 21]
[ 6 13 23]
...
[ 6 9 16]
[ 9 9 17]
[ 9 9 17]]
Cela signifie que:
- Le premier pixel en haut à gauche est codé par le RVB : (10,16,28) ➙ 10% de Rouge, 16% de Vert et 28% de Bleu;
- Le pixel qui est juste à droite du premier est codé par le RVB : (3,9,21);
- Le pixel qui est juste en-dessous du premier est codé par (6,9,16);
- etc.
Mieux comprendre la matrice de l’image
Pour simplifier, si notre image faisait 3 pixels sur 3, la matrice pourrait être par exemple:
image_matrix = np.array([
[[255, 0, 0], [0, 255, 0], [0, 0, 255]], # Première ligne de pixels
[[255, 255, 0], [0, 255, 255], [255, 0, 255]], # Deuxième ligne de pixels
[[128, 128, 128], [255, 255, 255], [0, 0, 0]] # Troisième ligne de pixels
])
Le chiffrement de l’image avec python
Maintenant, il est temps de chiffrer chaque nombre qui compose cette matrice. Là, nous avons le choix:
- Chiffrement de Vigenère
- Chiffrement de Hill
- Chiffrement affine
- Chiffrement AES (Advanced Encryption Standard)
- Chiffrement par permutation
- Chiffrement par substitution
- Chiffrement par diffusion
Je vais utiliser un chiffrement par permutation. Pour des raisons de sécurité, il est nécessaire d’ajouter une clé, afin que le déchiffrement ne soit pas possible sans.
from PIL import Image import numpy as np # Charger l'image image_path = 'ruban.jpg' image = Image.open(image_path) # Convertir l'image en matrice numpy image_matrix = np.array(image) # Fonction pour générer une permutation déterministe à partir d'une clé def generate_permutation(size, key): np.random.seed(key) return np.random.permutation(size) # Appliquer la permutation def apply_permutation(image_matrix, permutation): flat_matrix = image_matrix.flatten() permuted_matrix = flat_matrix[permutation] return permuted_matrix.reshape(image_matrix.shape) # Inverser la permutation def inverse_permutation(permutation): inverse = np.zeros_like(permutation) inverse[permutation] = np.arange(len(permutation)) return inverse # Clé de chiffrement key = 12345 # Vous pouvez utiliser n'importe quelle valeur comme clé # Générer une permutation pour l'image à partir de la clé permutation = generate_permutation(image_matrix.size, key) # Appliquer la permutation pour chiffrer l'image encrypted_matrix = apply_permutation(image_matrix, permutation) # Convertir la matrice chiffrée en image encrypted_image = Image.fromarray(encrypted_matrix.astype('uint8'), 'RGB') # Sauvegarder l'image chiffrée encrypted_image.save('ruban_chiffre_permutation.jpg') # Inverser la permutation pour déchiffrer l'image inverse_permutation = inverse_permutation(permutation) decrypted_matrix = apply_permutation(encrypted_matrix, inverse_permutation) # Convertir la matrice déchiffrée en image decrypted_image = Image.fromarray(decrypted_matrix.astype('uint8'), 'RGB') # Sauvegarder l'image déchiffrée decrypted_image.save('ruban_dechiffre_permutation.jpg')
L’image ainsi chiffrée est:
On peut maintenant se servir de ce code pour créer une interface graphique qui demande à l’utilisateur une image, puis une clé, puis un répertoire de sauvegarde.
Là, il a fallu adapter le code précédent car la méthode de chiffrement n’était pas compatible. Je change aussi quelques petits trucs concernant la clé de chiffrement qui, sur les codes précédents, ne peuvent être que des chaînes numériques. J’aimerais que la clé puisse être alphanumérique.
import tkinter as tk from tkinter import filedialog, messagebox from PIL import Image import numpy as np import hashlib # Convertir une chaine de caractères en entier def key_from_string(key_string): # Convertir la chaîne en une valeur de hachage (MD5) hashed_key = hashlib.md5(key_string.encode()).hexdigest() # Convertir la valeur hexadécimale en entier key_int = int(hashed_key, 16) # Réduire la valeur pour qu'elle soit compatible avec np.random.seed return key_int % (2**32) # Limiter à 32 bits # Fonction pour générer une permutation déterministe à partir d'une clé def generate_permutation(size, key): np.random.seed(key) return np.random.permutation(size) # Fonction pour appliquer la permutation def apply_permutation(image_matrix, permutation): # Si l'image est en 3D (RGB), appliquer la permutation sur chaque canal if len(image_matrix.shape) == 3: permuted_matrix = np.zeros_like(image_matrix) for channel in range(image_matrix.shape[2]): # Parcourir R, G, B flat_channel = image_matrix[:, :, channel].flatten() permuted_channel = flat_channel[permutation] permuted_matrix[:, :, channel] = permuted_channel.reshape(image_matrix.shape[:2]) return permuted_matrix else: # Si l'image est en niveaux de gris (2D) flat_matrix = image_matrix.flatten() permuted_matrix = flat_matrix[permutation] return permuted_matrix.reshape(image_matrix.shape) # Fonction pour inverser la permutation def inverse_permutation(permutation): inverse = np.zeros_like(permutation) inverse[permutation] = np.arange(len(permutation)) return inverse # Fonction pour chiffrer l'image def encrypt_image(image_path, key, save_path): image = Image.open(image_path) image_matrix = np.array(image) # Si la clé est une chaîne, la convertir en entier if isinstance(key, str): key = key_from_string(key) # Générer une permutation permutation = generate_permutation(image_matrix.shape[0] * image_matrix.shape[1], key) # Appliquer la permutation encrypted_matrix = apply_permutation(image_matrix, permutation) # Sauvegarder l'image chiffrée encrypted_image = Image.fromarray(encrypted_matrix.astype('uint8')) encrypted_image.save(save_path) messagebox.showinfo("Succès", "L'image a été chiffrée et sauvegardée avec succès.") # Fonction pour déchiffrer l'image def decrypt_image(image_path, key, save_path): image = Image.open(image_path) image_matrix = np.array(image) # Si la clé est une chaîne, la convertir en entier if isinstance(key, str): key = key_from_string(key) # Générer la permutation et son inverse permutation = generate_permutation(image_matrix.shape[0] * image_matrix.shape[1], key) inverse_perm = inverse_permutation(permutation) # Appliquer l'inverse de la permutation decrypted_matrix = apply_permutation(image_matrix, inverse_perm) # Sauvegarder l'image déchiffrée decrypted_image = Image.fromarray(decrypted_matrix.astype('uint8')) decrypted_image.save(save_path) messagebox.showinfo("Succès", "L'image a été déchiffrée et sauvegardée avec succès.") # Fonction pour sélectionner une image def select_image(): file_path = filedialog.askopenfilename(title="Sélectionner une image", filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")]) if file_path: image_path_entry.delete(0, tk.END) image_path_entry.insert(0, file_path) # Fonction pour sélectionner un répertoire de sauvegarde def select_save_directory(): dir_path = filedialog.askdirectory(title="Sélectionner un répertoire de sauvegarde") if dir_path: save_dir_entry.delete(0, tk.END) save_dir_entry.insert(0, dir_path) # Fonction pour chiffrer l'image def encrypt(): image_path = image_path_entry.get() key = key_entry.get() save_dir = save_dir_entry.get() save_path = f"{save_dir}/encrypted_image.jpg" encrypt_image(image_path, key, save_path) def decrypt(): image_path = image_path_entry.get() key = key_entry.get() save_dir = save_dir_entry.get() save_path = f"{save_dir}/decrypted_image.jpg" decrypt_image(image_path, key, save_path) # Créer la fenêtre principale root = tk.Tk() root.title("Chiffrement et Déchiffrement d'Image") # Créer les widgets image_path_label = tk.Label(root, text="Chemin de l'image:") image_path_label.grid(row=0, column=0, padx=10, pady=10) image_path_entry = tk.Entry(root, width=50) image_path_entry.grid(row=0, column=1, padx=10, pady=10) image_path_button = tk.Button(root, text="Sélectionner une image", command=select_image) image_path_button.grid(row=0, column=2, padx=10, pady=10) key_label = tk.Label(root, text="Clé de chiffrement:") key_label.grid(row=1, column=0, padx=10, pady=10) key_entry = tk.Entry(root, width=50) key_entry.grid(row=1, column=1, padx=10, pady=10) save_dir_label = tk.Label(root, text="Répertoire de sauvegarde:") save_dir_label.grid(row=2, column=0, padx=10, pady=10) save_dir_entry = tk.Entry(root, width=50) save_dir_entry.grid(row=2, column=1, padx=10, pady=10) save_dir_button = tk.Button(root, text="Sélectionner un répertoire", command=select_save_directory) save_dir_button.grid(row=2, column=2, padx=10, pady=10) encrypt_button = tk.Button(root, text="Chiffrer l'image", command=encrypt) encrypt_button.grid(row=3, column=1, padx=10, pady=10) decrypt_button = tk.Button(root, text="Déchiffrer l'image", command=decrypt) decrypt_button.grid(row=3, column=2, padx=10, pady=10) # Lancer la boucle principale root.mainloop()
Et voilà! ça fonctionne nickel!