Chiffre de César en Java: guide de mise en œuvre complet
Apprenez à implémenter le chiffre César en Java avec des exemples de code complets. Couvre le chiffrement de base, la conception POO avec une classe CaesarCipher, le chiffrement de fichiers, les tests unitaires et un outil de ligne de commande.
Le chiffre César est l'une des techniques de chiffrement les plus simples et les plus connues en cryptographie. Pour les développeurs Java, l'implémentation de ce chiffre offre une excellente opportunité de mettre en pratique les fonctionnalités de base du langage, notamment la programmation orientée objet, les E/S de fichiers, la manipulation de chaînes et les tests unitaires. Que vous soyez un étudiant en informatique effectuant un devoir en classe ou un développeur révisant les principes fondamentaux de la cryptographie, ce guide vous guide à travers des implémentations Java de plus en plus sophistiquées.
Dans ce didacticiel, vous allez créer cinq programmes complets: une fonction de chiffrement/déchiffrement de base, une classe CaesarCipher entièrement orientée objet, un utilitaire de chiffrement de fichiers, une suite de tests JUnit et un outil de ligne de commande perfectionné. Chaque exemple de code est compilé et exécuté avec Java 17 standard ou version ultérieure.
Essayez-le en ligne: Vous souhaitez expérimenter le chiffrement César avant d'écrire du code? Utilisez notre encodeur du chiffre César gratuit pour voir comment différentes valeurs de décalage transforment votre texte.
Comprendre la logique du chiffrement César en Java
Le chiffre César fonctionne en décalant chaque lettre du texte clair d’un nombre fixe de positions dans l’alphabet. Par exemple, avec un décalage de 3, la lettre A devient D, B devient E, et ainsi de suite. Les lettres s'enroulent de telle sorte que X devient A, Y devient B et Z devient C.
En Java, vous pouvez tirer parti du fait que les valeurs char sont numériques. Les lettres majuscules A à Z occupent les valeurs ASCII 65 à 90 et les lettres minuscules a à z occupent 97 à 122. En effectuant des opérations arithmétiques sur ces valeurs de caractères et en utilisant l'opérateur modulo, vous pouvez implémenter le décalage proprement sans tables de recherche.
La formule de cryptage de base est la suivante:
encryptedChar = (char - base + shift) % 26 + base
où base est 'A' pour les majuscules ou 'a' pour les lettres minuscules, et shift est le nombre de positions à avancer. Le décryptage utilise simplement le décalage inverse (26 moins le décalage de chiffrement).
Implémentation de base du chiffre César
L'approche la plus simple utilise deux méthodes statiques pour le cryptage et le déchiffrement. Cette version gère à la fois les lettres majuscules et minuscules tout en laissant intacts les caractères non alphabétiques.
public class BasicCaesarCipher {
public static String encrypt(String plaintext, int shift) {
// Normalize shift to 0-25 range
shift = ((shift % 26) + 26) % 26;
StringBuilder result = new StringBuilder();
for (char ch : plaintext.toCharArray()) {
if (Character.isUpperCase(ch)) {
char encrypted = (char) ((ch - 'A' + shift) % 26 + 'A');
result.append(encrypted);
} else if (Character.isLowerCase(ch)) {
char encrypted = (char) ((ch - 'a' + shift) % 26 + 'a');
result.append(encrypted);
} else {
// Non-alphabetic characters remain unchanged
result.append(ch);
}
}
return result.toString();
}
public static String decrypt(String ciphertext, int shift) {
// Decryption is encryption with the inverse shift
return encrypt(ciphertext, 26 - (((shift % 26) + 26) % 26));
}
public static void main(String[] args) {
String message = "Hello, World! The quick brown fox jumps over the lazy dog.";
int shift = 7;
String encrypted = encrypt(message, shift);
String decrypted = decrypt(encrypted, shift);
System.out.println("Original: " + message);
System.out.println("Encrypted: " + encrypted);
System.out.println("Decrypted: " + decrypted);
}
}
Lorsque vous exécutez ce programme, le résultat est:
Original: Hello, World! The quick brown fox jumps over the lazy dog.
Encrypted: Olssv, Dvysk! Aol xbpjr iyvdu mve qbtwz vcly aol shgf kvn.
Decrypted: Hello, World! The quick brown fox jumps over the lazy dog.
Quelques détails importants dans cette implémentation:
- Normalisation du décalage: l'expression
((shift % 26) + 26) % 26garantit que le décalage est toujours compris entre 0 et 25, même lorsque des valeurs négatives sont transmises. L'opérateur modulo de Java peut renvoyer des valeurs négatives pour les opérandes négatifs, de sorte que le modèle double modulo gère ce cas limite. - StringBuilder: l'utilisation de
StringBuilderplutôt que la concaténation de chaînes est plus efficace pour construire le résultat caractère par caractère, en particulier avec des textes plus longs. - Préservation des caractères: les chiffres, la ponctuation, les espaces et tout autre caractère autre que des lettres sont transmis sans modification, ce qui est le comportement standard pour les implémentations classiques du chiffrement César.
Classe CaesarCipher orientée objet
Pour les projets Java réels, une conception orientée objet est plus appropriée. La classe CaesarCipher suivante encapsule la valeur de décalage en tant qu'état d'instance, prend en charge le chaînage de méthodes et fournit des méthodes utilitaires supplémentaires, notamment le déchiffrement par force brute.
import java.util.ArrayList;
import java.util.List;
public class CaesarCipher {
private final int shift;
/**
* Creates a CaesarCipher with the given shift value.
* The shift is normalized to the 0-25 range.
*
* @param shift the number of positions to shift letters
*/
public CaesarCipher(int shift) {
this.shift = ((shift % 26) + 26) % 26;
}
/**
* Returns the normalized shift value.
*/
public int getShift() {
return shift;
}
/**
* Encrypts the given plaintext using this cipher's shift value.
*
* @param plaintext the text to encrypt
* @return the encrypted ciphertext
*/
public String encrypt(String plaintext) {
if (plaintext == null) {
throw new IllegalArgumentException("Input text cannot be null");
}
return transform(plaintext, shift);
}
/**
* Decrypts the given ciphertext using this cipher's shift value.
*
* @param ciphertext the text to decrypt
* @return the decrypted plaintext
*/
public String decrypt(String ciphertext) {
if (ciphertext == null) {
throw new IllegalArgumentException("Input text cannot be null");
}
return transform(ciphertext, 26 - shift);
}
/**
* Performs a brute force attack, returning all 26 possible decryptions.
*
* @param ciphertext the text to brute force
* @return a list of all possible decryptions with their shift values
*/
public static List<String> bruteForce(String ciphertext) {
List<String> results = new ArrayList<>();
for (int s = 0; s < 26; s++) {
CaesarCipher cipher = new CaesarCipher(s);
results.add("Shift " + String.format("%2d", s) + ": " + cipher.decrypt(ciphertext));
}
return results;
}
private static String transform(String text, int shift) {
StringBuilder sb = new StringBuilder(text.length());
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch >= 'A' && ch <= 'Z') {
sb.append((char) ((ch - 'A' + shift) % 26 + 'A'));
} else if (ch >= 'a' && ch <= 'z') {
sb.append((char) ((ch - 'a' + shift) % 26 + 'a'));
} else {
sb.append(ch);
}
}
return sb.toString();
}
@Override
public String toString() {
return "CaesarCipher{shift=" + shift + "}";
}
public static void main(String[] args) {
CaesarCipher cipher = new CaesarCipher(13);
System.out.println(cipher);
String original = "Attack at dawn!";
String encrypted = cipher.encrypt(original);
String decrypted = cipher.decrypt(encrypted);
System.out.println("Original: " + original);
System.out.println("Encrypted: " + encrypted);
System.out.println("Decrypted: " + decrypted);
System.out.println("\n--- Brute Force Attack ---");
String mystery = "Xlmw mw e wigvix qiwweki!";
List<String> attempts = CaesarCipher.bruteForce(mystery);
for (String attempt : attempts) {
System.out.println(attempt);
}
}
}
Cette conception de classe suit plusieurs bonnes pratiques Java:
- Immuabilité: le champ
shiftestfinal, ce qui rend les instances thread-safe et prévisibles. - Validation: une entrée nulle génère immédiatement
IllegalArgumentExceptionplutôt que de provoquer unNullPointerExceptionplus profond dans le code. - Encapsulation: la méthode
transformest privée car il s'agit d'un détail d'implémentation dont les consommateurs de la classe n'ont pas besoin. - Utilitaire statique: la méthode
bruteForceest statique car elle ne dépend pas de la valeur de décalage d'une instance particulière.
Cryptage de fichiers avec le chiffre César
Le traitement des fichiers est une exigence courante pour les implémentations de chiffrement. Le programme suivant lit un fichier texte, crypte son contenu et écrit le résultat dans un nouveau fichier. Il utilise Java NIO pour une gestion de fichiers moderne et efficace.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class CaesarFileEncryptor {
/**
* Encrypts the contents of a file and writes the result to an output file.
*
* @param inputPath path to the plaintext file
* @param outputPath path for the encrypted output file
* @param shift the Caesar cipher shift value
* @throws IOException if file reading or writing fails
*/
public static void encryptFile(Path inputPath, Path outputPath, int shift)
throws IOException {
CaesarCipher cipher = new CaesarCipher(shift);
String content = Files.readString(inputPath, StandardCharsets.UTF_8);
String encrypted = cipher.encrypt(content);
Files.writeString(outputPath, encrypted,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
}
/**
* Decrypts the contents of a file and writes the result to an output file.
*
* @param inputPath path to the encrypted file
* @param outputPath path for the decrypted output file
* @param shift the Caesar cipher shift value used for encryption
* @throws IOException if file reading or writing fails
*/
public static void decryptFile(Path inputPath, Path outputPath, int shift)
throws IOException {
CaesarCipher cipher = new CaesarCipher(shift);
String content = Files.readString(inputPath, StandardCharsets.UTF_8);
String decrypted = cipher.decrypt(content);
Files.writeString(outputPath, decrypted,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
}
public static void main(String[] args) {
if (args.length < 4) {
System.err.println("Usage: java CaesarFileEncryptor <encrypt|decrypt> <shift> <input> <output>");
System.exit(1);
}
String mode = args[0].toLowerCase();
int shift = Integer.parseInt(args[1]);
Path input = Path.of(args[2]);
Path output = Path.of(args[3]);
try {
if ("encrypt".equals(mode)) {
encryptFile(input, output, shift);
System.out.println("File encrypted successfully: " + output);
} else if ("decrypt".equals(mode)) {
decryptFile(input, output, shift);
System.out.println("File decrypted successfully: " + output);
} else {
System.err.println("Invalid mode. Use 'encrypt' or 'decrypt'.");
System.exit(1);
}
} catch (IOException e) {
System.err.println("Error processing file: " + e.getMessage());
System.exit(1);
}
}
}
Utilisation depuis la ligne de commande:
# Encrypt a file with shift of 5
java CaesarFileEncryptor encrypt 5 secret.txt secret.enc
# Decrypt the file
java CaesarFileEncryptor decrypt 5 secret.enc secret_decrypted.txt
Cette implémentation utilise Files.readString() et Files.writeString(), disponibles dans Java 11 et versions ultérieures. Pour les fichiers très volumineux qui ne tiennent pas en mémoire, vous souhaiterez plutôt utiliser une approche de streaming avec BufferedReader et BufferedWriter.
Tests unitaires avec JUnit 5
Des tests approfondis sont essentiels pour toute mise en œuvre du chiffrement. La classe de test JUnit 5 suivante couvre le fonctionnement normal, les cas extrêmes et la relation entre le chiffrement et le déchiffrement.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
class CaesarCipherTest {
@Test
@DisplayName("Encrypt with shift of 3 produces correct output")
void testBasicEncryption() {
CaesarCipher cipher = new CaesarCipher(3);
assertEquals("Khoor Zruog", cipher.encrypt("Hello World"));
}
@Test
@DisplayName("Decrypt reverses encryption")
void testDecryptReversesEncrypt() {
CaesarCipher cipher = new CaesarCipher(7);
String original = "The quick brown fox jumps over the lazy dog.";
String encrypted = cipher.encrypt(original);
assertEquals(original, cipher.decrypt(encrypted));
}
@ParameterizedTest
@ValueSource(ints = {0, 1, 5, 13, 25, -1, -13, 52, -52})
@DisplayName("Encrypt then decrypt returns original for all shift values")
void testRoundTripAllShifts(int shift) {
CaesarCipher cipher = new CaesarCipher(shift);
String original = "ABCxyz 123!@#";
assertEquals(original, cipher.decrypt(cipher.encrypt(original)));
}
@Test
@DisplayName("Non-alphabetic characters are preserved")
void testNonAlphaPreserved() {
CaesarCipher cipher = new CaesarCipher(10);
String input = "12345 !@#$% \t\n";
assertEquals(input, cipher.encrypt(input));
}
@Test
@DisplayName("Empty string returns empty string")
void testEmptyString() {
CaesarCipher cipher = new CaesarCipher(5);
assertEquals("", cipher.encrypt(""));
assertEquals("", cipher.decrypt(""));
}
@Test
@DisplayName("Shift of 0 returns original text")
void testShiftZero() {
CaesarCipher cipher = new CaesarCipher(0);
String text = "No change expected!";
assertEquals(text, cipher.encrypt(text));
}
@Test
@DisplayName("Shift of 26 is equivalent to shift of 0")
void testShiftTwentySix() {
CaesarCipher cipher = new CaesarCipher(26);
assertEquals(0, cipher.getShift());
assertEquals("Hello", cipher.encrypt("Hello"));
}
@ParameterizedTest
@CsvSource({
"abc, 1, bcd",
"xyz, 3, abc",
"ABC, 1, BCD",
"XYZ, 3, ABC",
"Hello, 13, Uryyb"
})
@DisplayName("Known encryption pairs are correct")
void testKnownPairs(String input, int shift, String expected) {
CaesarCipher cipher = new CaesarCipher(shift);
assertEquals(expected, cipher.encrypt(input));
}
@Test
@DisplayName("Null input throws IllegalArgumentException")
void testNullInput() {
CaesarCipher cipher = new CaesarCipher(5);
assertThrows(IllegalArgumentException.class, () -> cipher.encrypt(null));
assertThrows(IllegalArgumentException.class, () -> cipher.decrypt(null));
}
@Test
@DisplayName("Brute force returns 26 results")
void testBruteForceSize() {
var results = CaesarCipher.bruteForce("test");
assertEquals(26, results.size());
}
@Test
@DisplayName("Case is preserved during encryption")
void testCasePreservation() {
CaesarCipher cipher = new CaesarCipher(5);
String encrypted = cipher.encrypt("HeLLo WoRLd");
assertEquals("MjQQt BtWQi", encrypted);
}
}
Exécutez les tests avec Maven ou Gradle:
# Maven
mvn test
# Gradle
gradle test
La suite de tests couvre plusieurs catégories importantes:
- Exactement: vérification des sorties de chiffrement connues pour des entrées et des valeurs de décalage spécifiques.
- Intégrité aller-retour: garantir que le déchiffrement d'un message chiffré renvoie le texte original pour chaque valeur de décalage possible.
- Cas extrêmes: chaînes vides, décalage de zéro, décalage de 26, décalages négatifs et décalages supérieurs à 26.
- Gestion des caractères: les caractères non alphabétiques sont transmis sans modification et la casse des lettres est préservée.
- Gestion des erreurs: l'entrée nulle lève l'exception attendue.
Création d'un outil de ligne de commande
Le dernier exemple rassemble tout dans une application de ligne de commande raffinée avec une analyse des arguments, plusieurs modes de fonctionnement et une sortie conviviale. Cet outil prend en charge le cryptage, le déchiffrement et le craquage par force brute.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Scanner;
public class CaesarCipherCLI {
private static final String VERSION = "1.0.0";
public static void main(String[] args) {
if (args.length == 0) {
printUsage();
System.exit(0);
}
String command = args[0].toLowerCase();
switch (command) {
case "encrypt", "enc", "e" -> handleEncrypt(args);
case "decrypt", "dec", "d" -> handleDecrypt(args);
case "bruteforce", "brute", "bf" -> handleBruteForce(args);
case "interactive", "i" -> handleInteractive();
case "--version", "-v" -> System.out.println("Caesar Cipher CLI v" + VERSION);
case "--help", "-h" -> printUsage();
default -> {
System.err.println("Unknown command: " + command);
printUsage();
System.exit(1);
}
}
}
private static void handleEncrypt(String[] args) {
if (args.length < 3) {
System.err.println("Usage: encrypt <shift> <text|--file path>");
System.exit(1);
}
int shift = parseShift(args[1]);
String text = resolveText(args, 2);
CaesarCipher cipher = new CaesarCipher(shift);
System.out.println(cipher.encrypt(text));
}
private static void handleDecrypt(String[] args) {
if (args.length < 3) {
System.err.println("Usage: decrypt <shift> <text|--file path>");
System.exit(1);
}
int shift = parseShift(args[1]);
String text = resolveText(args, 2);
CaesarCipher cipher = new CaesarCipher(shift);
System.out.println(cipher.decrypt(text));
}
private static void handleBruteForce(String[] args) {
if (args.length < 2) {
System.err.println("Usage: bruteforce <text|--file path>");
System.exit(1);
}
String text = resolveText(args, 1);
System.out.println("=== Brute Force Results ===\n");
var results = CaesarCipher.bruteForce(text);
for (String result : results) {
System.out.println(result);
}
}
private static void handleInteractive() {
Scanner scanner = new Scanner(System.in);
System.out.println("Caesar Cipher Interactive Mode");
System.out.println("Type 'quit' to exit.\n");
while (true) {
System.out.print("Enter command (encrypt/decrypt/bruteforce): ");
String command = scanner.nextLine().trim().toLowerCase();
if ("quit".equals(command) || "exit".equals(command)) {
System.out.println("Goodbye!");
break;
}
if ("bruteforce".equals(command) || "bf".equals(command)) {
System.out.print("Enter ciphertext: ");
String text = scanner.nextLine();
var results = CaesarCipher.bruteForce(text);
results.forEach(System.out::println);
} else if ("encrypt".equals(command) || "decrypt".equals(command)) {
System.out.print("Enter shift value: ");
int shift = Integer.parseInt(scanner.nextLine().trim());
System.out.print("Enter text: ");
String text = scanner.nextLine();
CaesarCipher cipher = new CaesarCipher(shift);
String result = "encrypt".equals(command)
? cipher.encrypt(text)
: cipher.decrypt(text);
System.out.println("Result: " + result);
} else {
System.out.println("Unknown command. Try encrypt, decrypt, or bruteforce.");
}
System.out.println();
}
scanner.close();
}
private static String resolveText(String[] args, int startIndex) {
if ("--file".equals(args[startIndex])) {
if (args.length <= startIndex + 1) {
System.err.println("Error: --file requires a file path argument.");
System.exit(1);
}
try {
return Files.readString(Path.of(args[startIndex + 1]), StandardCharsets.UTF_8);
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
System.exit(1);
return "";
}
}
// Join remaining arguments as the text
StringBuilder sb = new StringBuilder();
for (int i = startIndex; i < args.length; i++) {
if (i > startIndex) sb.append(" ");
sb.append(args[i]);
}
return sb.toString();
}
private static int parseShift(String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
System.err.println("Error: Shift must be an integer. Got: " + value);
System.exit(1);
return 0;
}
}
private static void printUsage() {
System.out.println("""
Caesar Cipher CLI v%s
Usage:
caesar encrypt <shift> <text> Encrypt text with given shift
caesar decrypt <shift> <text> Decrypt text with given shift
caesar bruteforce <text> Try all 26 possible shifts
caesar encrypt <shift> --file <path> Encrypt a file's contents
caesar interactive Start interactive mode
Options:
--help, -h Show this help message
--version, -v Show version number
Examples:
caesar encrypt 3 "Hello World"
caesar decrypt 3 "Khoor Zruog"
caesar bruteforce "Khoor Zruog"
caesar encrypt 5 --file secret.txt
""".formatted(VERSION));
}
}
Compilez et exécutez l'outil:
# Compile all files
javac CaesarCipher.java CaesarCipherCLI.java
# Encrypt a message
java CaesarCipherCLI encrypt 13 "Meet me at midnight"
# Decrypt a message
java CaesarCipherCLI decrypt 13 "Zrrg zr ng zvqavtug"
# Brute force an unknown cipher
java CaesarCipherCLI bruteforce "Wklv lv d vhfuhw phvvdjh"
# Encrypt from a file
java CaesarCipherCLI encrypt 7 --file message.txt
# Interactive mode
java CaesarCipherCLI interactive
Cet outil de ligne de commande présente plusieurs fonctionnalités Java avancées:
- Expressions de commutation avec syntaxe de flèche (Java 14+)
- Blocs de texte pour les chaînes multilignes (Java 15+)
- Formatage de chaîne avec la méthode
formatted() - Var pour les variables locales où le type est clair du contexte
Considérations relatives aux performances
Le chiffre César lui-même est trivial sur le plan informatique avec une complexité temporelle O(n) où n est la longueur du texte saisi. Cependant, il existe quelques conseils en matière de performances qui méritent d'être pris en compte pour les implémentations Java:
Capacité StringBuilder: lorsque vous connaissez la longueur de sortie à l'avance (elle est égale à la longueur d'entrée pour le chiffre César), pré-attribuez le StringBuilder avec new StringBuilder(text.length()). Cela évite le redimensionnement du tampon interne.
Tableau de caractères vs charAt: pour les chaînes très longues, la conversion en char[] avec toCharArray() et l'itération sur le tableau peuvent être légèrement plus rapides que l'appel de charAt(i) dans une boucle, car cela évite la vérification des limites à chaque accès. Cependant, cela crée une copie supplémentaire des données de chaîne.
Approche basée sur les flux: bien que vous puissiez utiliser Java Streams pour traiter les caractères, la boucle traditionnelle est plus rapide pour ce type de transformation caractère par caractère en raison de la surcharge liée au boxing des valeurs char en objets Character.
Pièges courants et comment les éviter
Lors de l'implémentation du chiffre César en Java, faites attention à ces erreurs fréquentes:
Modulo négatif: l'opérateur % de Java renvoie des résultats négatifs pour les opérandes négatifs. L'expression -3 % 26 est évaluée à -3 et non à 23. Normalisez toujours avec le modèle double modulo: ((value % 26) + 26) % 26.
Dépassement de type de caractère: lors de l'ajout d'un décalage à une valeur char, le résultat est promu à int. Vous devez le reconvertir explicitement en char, sinon votre StringBuilder ajoutera la valeur entière au lieu du caractère.
Oublier de gérer les deux cas: Un bug courant consiste à chiffrer correctement les lettres majuscules mais à oublier les lettres minuscules (ou vice versa). Testez toujours avec une entrée à casse mixte.
Codage en dur de la taille de l'alphabet: L'utilisation du nombre magique 26 dans tout votre code est standard pour le chiffrement César anglais, mais pensez à le définir comme une constante nommée (private static final int ALPHABET_SIZE = 26;) pour plus de clarté et de maintenabilité.
Résumé
Vous disposez désormais d’une boîte à outils Java complète pour les opérations de chiffrement César. L'implémentation de base démontre la manipulation des chaînes de base. La classe POO fournit un composant réutilisable et bien testé. Le chiffreur de fichiers montre Java NIO en action. Les tests JUnit garantissent l’exactitude dans tous les cas extrêmes. Et l’outil de ligne de commande relie le tout dans une application pratique.
Ces implémentations constituent une base solide pour explorer des chiffrements plus avancés. Vous pouvez étendre la classe CaesarCipher pour prendre en charge les alphabets personnalisés, ajouter une analyse de fréquence pour la détection automatique des clés ou l'utiliser comme élément de base pour le chiffre Vigenere, qui applique différents décalages César à différentes positions dans le texte.
Prochaines étapes: Prêt à explorer davantage de chiffrements? Découvrez le Chiffre Vigenere, qui étend le concept de chiffrement César avec plusieurs valeurs de décalage pour un cryptage beaucoup plus puissant.