Numérique et Sciences Informatiques1ère

Cryptographie basique
Exercices corrigés

Maîtrisez la cryptographie basique : chiffrement, déchiffrement, clés, algorithmes, sécurité des données grâce à ces 5 exercices détaillés.

Concepts & Exercices
Cryptographie = Clé × Algorithme × Message × Sécurité
Formule de la cryptographie basique
Chiffrement
→ Transformation
Protéger les données
Clé
→ Sécurité
Secret de chiffrement
Déchiffrement
→ Récupération
Accès au message
Processus de chiffrement/déchiffrement

Message → Algorithme → Clé → Texte chiffré → Algorithme → Clé → Message original

Exercice 1
Implémenter un chiffrement de César avec une clé personnalisée.
Exercice 2
Créer un système de chiffrement asymétrique basique avec clés publiques/privées.
Exercice 3
Analyser la sécurité d'un algorithme de chiffrement par rapport aux attaques.
Exercice 4
Comparer différents algorithmes de hachage et leur résistance aux collisions.
Exercice 5
Étudier les protocoles de sécurité pour la transmission de données chiffrées.
Corrigé : Exercices 1 à 3
1 Chiffrement de César
Définition du chiffrement de César :

Chiffrement de César : Technique de chiffrement par substitution où chaque lettre est décalée de n positions dans l'alphabet.

Méthodologie d'analyse :
  1. Identifier le message à chiffrer
  2. Déterminer la clé de décalage
  3. Appliquer le décalage à chaque caractère
  4. Conserver les caractères non alphabétiques
Composants du chiffrement de César
  • Message original (clair)
  • Clé de décalage (n)
  • Alphabet de substitution
  • Message chiffré
Étape 1 : Analyse du message

Identifier les caractères à chiffrer et les positions dans l'alphabet.

Étape 2 : Application du décalage

Pour chaque lettre, calculer la nouvelle position : (position + clé) % 26.

Étape 3 : Gestion des majuscules/minuscules

Préserver la casse des lettres lors du chiffrement.

Algorithme de substitution alphabétique
Réponse finale :
def chiffrement_cesar(texte, cle):
    resultat = ""
    for caractere in texte:
        if caractere.isalpha():
            # Déterminer si c'est majuscule ou minuscule
            base = ord('A') if caractere.isupper() else ord('a')
            # Chiffrer la lettre
            lettre_chiffree = chr((ord(caractere) - base + cle) % 26 + base)
            resultat += lettre_chiffree
        else:
            # Conserver les caractères non alphabétiques
            resultat += caractere
    return resultat

def dechiffrement_cesar(texte, cle):
    # Inverse du chiffrement
    return chiffrement_cesar(texte, -cle)

# Exemple d'utilisation
message = "Bonjour le monde!"
cle = 3
message_chiffre = chiffrement_cesar(message, cle)
message_dechiffre = dechiffrement_cesar(message_chiffre, cle)

print(f"Message original: {message}")
print(f"Message chiffré: {message_chiffre}")
print(f"Message déchiffré: {message_dechiffre}")
Lois et principes :

Loi du décalage : Chaque lettre est décalée de n positions

Principe de modularité : Utilisation de modulo 26 pour les 26 lettres

Synergie alphabétique : Le chiffrement préserve la structure du message

2 Système de chiffrement asymétrique basique
Définition du chiffrement asymétrique :

Chiffrement asymétrique : Système utilisant une paire de clés (publique/privée) pour le chiffrement/déchiffrement.

Caractéristiques du chiffrement asymétrique
  • Clé publique pour le chiffrement
  • Clé privée pour le déchiffrement
  • Impossible de déduire la clé privée de la clé publique
  • Basé sur des problèmes mathématiques complexes
Étape 1 : Génération des clés
def generer_cles_rsa():
    # Choix de nombres premiers (pour exemple simple)
    p, q = 61, 53
    n = p * q
    phi = (p-1) * (q-1)
    
    # Choix de l'exposant de chiffrement
    e = 17  # Doit être copremier avec phi
    
    # Calcul de l'exposant de déchiffrement
    d = inverse_modulaire(e, phi)
    
    return (e, n), (d, n)  # clé publique, clé privée
Étape 2 : Chiffrement
def chiffrer_rsa(message, cle_publique):
    e, n = cle_publique
    # Conversion du message en entiers
    message_int = [ord(char) for char in message]
    # Chiffrement de chaque caractère
    chiffre = [pow(char, e, n) for char in message_int]
    return chiffre
Étape 3 : Déchiffrement
def dechiffrer_rsa(chiffre, cle_privee):
    d, n = cle_privee
    # Déchiffrement de chaque entier
    message_int = [pow(c, d, n) for c in chiffre]
    # Conversion en chaîne de caractères
    message = ''.join(chr(char) for char in message_int)
    return message
Système à clés publiques/privées
Réponse finale :
def pgcd(a, b):
    """Calcul du PGCD"""
    while b:
        a, b = b, a % b
    return a

def inverse_modulaire(a, m):
    """Calcul de l'inverse modulaire"""
    if pgcd(a, m) != 1:
        return None
    
    for x in range(1, m):
        if (a * x) % m == 1:
            return x
    return None

def generer_cles_rsa():
    """Génération d'une paire de clés RSA (simplifié)"""
    # Nombres premiers choisis (en pratique, beaucoup plus grands)
    p, q = 61, 53
    n = p * q
    phi = (p - 1) * (q - 1)
    
    # Choix de e (doit être copremier avec phi)
    e = 17
    
    # Calcul de d (inverse modulaire de e modulo phi)
    d = inverse_modulaire(e, phi)
    
    cle_publique = (e, n)
    cle_privee = (d, n)
    
    return cle_publique, cle_privee

def chiffrer_rsa(message, cle_publique):
    """Chiffrement RSA"""
    e, n = cle_publique
    message_int = [ord(char) for char in message]
    chiffre = [pow(char, e, n) for char in message_int]
    return chiffre

def dechiffrer_rsa(chiffre, cle_privee):
    """Déchiffrement RSA"""
    d, n = cle_privee
    message_int = [pow(c, d, n) for c in chiffre]
    message = ''.join(chr(char) for char in message_int)
    return message

# Exemple d'utilisation
cle_publique, cle_privee = generer_cles_rsa()
message = "Hello World"
chiffre = chiffrer_rsa(message, cle_publique)
dechiffre = dechiffrer_rsa(chiffre, cle_privee)

print(f"Message original: {message}")
print(f"Message chiffré: {chiffre}")
print(f"Message déchiffré: {dechiffre}")
Lois et principes :

Principe de la paire de clés : Une clé pour chiffrer, une autre pour déchiffrer

Difficulté de calcul inverse : Impossible de déduire la clé privée de la publique

Base mathématique : Utilisation de problèmes mathématiques complexes

3 Analyse de la sécurité des algorithmes
Définition de la sécurité cryptographique :

Sécurité cryptographique : Capacité d'un algorithme à résister aux attaques visant à déchiffrer sans la clé.

Types d'attaques cryptographiques
  • Attaque par force brute
  • Attaque par analyse fréquentielle
  • Attaque par texte chiffré connu
  • Attaque par texte clair connu
Étape 1 : Analyse de la complexité

Évaluer le nombre de tentatives nécessaires pour casser le chiffrement.

Étape 2 : Résistance aux attaques

Tester la robustesse face aux différentes méthodes d'attaque.

Étape 3 : Comparaison des algorithmes

Comparer la sécurité des algorithmes selon leur complexité.

Sécurité relative à la complexité mathématique
Réponse finale :
def analyser_securite_algorithme(algorithme, longueur_cle):
    """Analyse de la sécurité d'un algorithme de chiffrement"""
    # Calcul du nombre de combinaisons possibles
    if algorithme == "cesar":
        # 26 possibilités pour le chiffrement de César
        combinaisons = 26
        nom_algorithme = "Chiffrement de César"
        description = "Très faible sécurité, cassable en quelques secondes"
    elif algorithme == "vigenere":
        # Approximation pour une clé de longueur donnée
        combinaisons = 26 ** longueur_cle
        nom_algorithme = "Chiffrement de Vigenère"
        description = f"Sécurité moyenne pour une clé de {longueur_cle} caractères"
    elif algorithme == "rsa":
        # Pour RSA, la sécurité dépend de la longueur de la clé
        combinaisons = 2 ** longueur_cle
        nom_algorithme = f"RSA ({longueur_cle}-bits)"
        description = f"Sécurité élevée pour une clé de {longueur_cle} bits"
    else:
        return "Algorithme inconnu"
    
    # Temps estimé pour casser (hypothétique)
    taux_tentatives_par_seconde = 1000000  # 1 million de tentatives/s
    temps_secondes = combinaisons / taux_tentatives_par_seconde
    
    if temps_secondes < 1:
        temps = "moins d'une seconde"
    elif temps_secondes < 60:
        temps = f"{temps_secondes:.1f} secondes"
    elif temps_secondes < 3600:
        temps = f"{temps_secondes/60:.1f} minutes"
    elif temps_secondes < 86400:
        temps = f"{temps_secondes/3600:.1f} heures"
    elif temps_secondes < 31536000:
        temps = f"{temps_secondes/86400:.1f} jours"
    else:
        temps = f"{temps_secondes/31536000:.1f} années"
    
    return f"{nom_algorithme}:\n- Combinaisons: {combinaisons}\n- Temps de cassage estimé: {temps}\n- Description: {description}"

# Comparaison des algorithmes
print(analyser_securite_algorithme("cesar", 1))
print("\n" + analyser_securite_algorithme("vigenere", 5))
print("\n" + analyser_securite_algorithme("rsa", 2048))
Lois et principes :

Loi de la complexité : Plus l'espace de clés est grand, plus l'algorithme est sûr

Principe de Kerckhoffs : La sécurité repose sur la clé, pas sur l'algorithme

Synergie de la longueur : Des clés plus longues augmentent la sécurité

Corrigé : Exercices 4 à 5
4 Algorithmes de hachage et résistance aux collisions
Définition des algorithmes de hachage :

Algorithmes de hachage : Fonctions qui transforment des données en une chaîne de longueur fixe, résistant aux collisions.

Propriétés des bons algorithmes de hachage
  • Unidirectionnel (impossible de retrouver les données)
  • Résistant aux collisions
  • Calcul rapide mais lent pour le brute-force
  • Utilisation de sel (salt) pour renforcer la sécurité
Étape 1 : Calcul de hachage
import hashlib

def calculer_hachage(message, algorithme='sha256'):
    """Calcule le hachage d'un message"""
    hacheur = hashlib.new(algorithme)
    hacheur.update(message.encode())
    return hacheur.hexdigest()
Étape 2 : Comparaison des algorithmes
def comparer_hachages(message):
    """Compare différents algorithmes de hachage"""
    resultats = {}
    algorithmes = ['md5', 'sha1', 'sha256', 'sha512']
    
    for algo in algorithmes:
        resultats[algo] = calculer_hachage(message, algo)
    
    return resultats
Étape 3 : Analyse des collisions
def tester_collision(algorithme, message1, message2):
    """Teste si deux messages produisent la même empreinte"""
    hache1 = calculer_hachage(message1, algorithme)
    hache2 = calculer_hachage(message2, algorithme)
    return hache1 == hache2
Algorithmes avec résistance variable aux collisions
Réponse finale :
import hashlib
import time

class ComparaisonHachage:
    def __init__(self):
        self.algorithmes = ['md5', 'sha1', 'sha256', 'sha512']
    
    def calculer_hachage(self, message, algorithme='sha256'):
        """Calcule le hachage d'un message"""
        try:
            hacheur = hashlib.new(algorithme)
            hacheur.update(message.encode())
            return hacheur.hexdigest()
        except ValueError:
            return f"Algorithme {algorithme} non supporté"
    
    def comparer_algorithmes(self, message):
        """Compare les hachages produits par différents algorithmes"""
        resultats = {}
        
        for algo in self.algorithmes:
            debut = time.time()
            hache = self.calculer_hachage(message, algo)
            temps_execution = time.time() - debut
            resultats[algo] = {
                'hache': hache,
                'temps': temps_execution,
                'longueur': len(hache)
            }
        
        return resultats
    
    def afficher_resultats(self, message):
        """Affiche les résultats de comparaison"""
        print(f"Message: {message}")
        print("-" * 60)
        
        resultats = self.comparer_algorithmes(message)
        
        for algo, info in resultats.items():
            print(f"{algo.upper()}:")
            print(f"  Haché: {info['hache']}")
            print(f"  Longueur: {info['longueur']} caractères")
            print(f"  Temps: {info['temps']:.6f}s")
            print()
    
    def tester_resistance_collision(self):
        """Teste la résistance aux collisions"""
        messages = [
            "Bonjour",
            "bonjour",  # Changement de casse
            "Bonjour!", # Ajout de caractère
            "Bonjour le monde!" # Message plus long
        ]
        
        print("Tests de résistance aux collisions:")
        print("-" * 40)
        
        for algo in self.algorithmes:
            print(f"\n{algo.upper()}:")
            haches = [self.calculer_hachage(msg, algo) for msg in messages]
            
            # Vérifier les collisions
            collisions = set()
            for i in range(len(haches)):
                for j in range(i+1, len(haches)):
                    if haches[i] == haches[j]:
                        collisions.add((messages[i], messages[j]))
            
            if collisions:
                print(f"  Collisions détectées: {collisions}")
            else:
                print(f"  Aucune collision détectée")

# Exemple d'utilisation
comparaison = ComparaisonHachage()
comparaison.afficher_resultats("Mot de passe secret")
comparaison.tester_resistance_collision()
Lois et principes :

Loi de l'unidirectionalité : Le hachage est irréversible

Principe de la résistance : Des entrées différentes doivent produire des sorties différentes

Synergie de la complexité : SHA-256 est plus sécurisé que MD5

5 Protocoles de sécurité pour la transmission chiffrée
Définition des protocoles de sécurité :

Protocoles de sécurité : Ensembles de règles et de procédures pour sécuriser la transmission de données.

Protocoles de sécurité courants
  • SSL/TLS (HTTPS)
  • SSH (Secure Shell)
  • IPSec (Internet Protocol Security)
  • WPA/WPA2 (Wi-Fi Protected Access)
Étape 1 : Établissement de la connexion
# Simulation d'échange de clés (simplifié)
def echanger_cles():
    """Simule l'échange de clés pour une session sécurisée"""
    # Génération de clés (simplifié)
    cle_session = "clé_secrete_generee"
    return cle_session
Étape 2 : Chiffrement des données
def chiffrer_transmission(donnees, cle_session):
    """Chiffre les données avant transmission"""
    # Utilisation d'un algorithme de chiffrement symétrique (simplifié)
    import base64
    donnees_bytes = donnees.encode()
    donnees_chiffrees = base64.b64encode(donnees_bytes)
    return donnees_chiffrees.decode()
Étape 3 : Transmission sécurisée
def transmettre_securise(donnees, cle_session):
    """Transmet des données de manière sécurisée"""
    donnees_chiffrees = chiffrer_transmission(donnees, cle_session)
    # Simulation d'envoi sur un canal sécurisé
    print(f"Données transmises: {donnees_chiffrees}")
    return donnees_chiffrees
Transmission de données chiffrées avec protocole de sécurité
Réponse finale :
import ssl
import socket
import hashlib
import hmac

class ProtocoleSecurise:
    def __init__(self):
        self.etat_connexion = "non_connecte"
        self.cle_session = None
        self.algorithme_hachage = "sha256"
    
    def etablir_connexion_securisee(self, hote, port):
        """Établit une connexion sécurisée avec SSL/TLS"""
        try:
            # Création d'une socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            
            # Enveloppement avec SSL
            contexte_ssl = ssl.create_default_context()
            contexte_ssl.check_hostname = False
            contexte_ssl.verify_mode = ssl.CERT_NONE
            
            sock_ssl = contexte_ssl.wrap_socket(sock, server_hostname=hote)
            sock_ssl.connect((hote, port))
            
            self.etat_connexion = "connecte"
            print(f"Connexion sécurisée établie avec {hote}:{port}")
            
            # Génération de clé de session
            self.generer_cle_session()
            
            return sock_ssl
        except Exception as e:
            print(f"Erreur de connexion : {e}")
            return None
    
    def generer_cle_session(self):
        """Génère une clé de session pour la communication"""
        import secrets
        self.cle_session = secrets.token_bytes(32)  # 256 bits
        print("Clé de session générée")
    
    def chiffrer_message(self, message):
        """Chiffre un message avec HMAC pour l'authentification"""
        message_bytes = message.encode()
        hache_mac = hmac.new(self.cle_session, message_bytes, hashlib.sha256).hexdigest()
        message_chiffre = message_bytes.hex() + hache_mac
        return message_chiffre
    
    def dechiffrer_message(self, message_chiffre):
        """Déchiffre un message et vérifie l'authenticité"""
        # Extraction de la signature HMAC
        longueur_hache = 64  # SHA-256 = 256 bits = 64 caractères hex
        message_hex = message_chiffre[:-longueur_hache]
        hache_recu = message_chiffre[-longueur_hache:]
        
        # Conversion en bytes
        message_bytes = bytes.fromhex(message_hex)
        
        # Vérification de l'authenticité
        hache_calcule = hmac.new(self.cle_session, message_bytes, hashlib.sha256).hexdigest()
        
        if hache_calcule == hache_recu:
            return message_bytes.decode(), True
        else:
            return "", False
    
    def envoyer_message(self, socket_ssl, message):
        """Envoie un message chiffré via la connexion sécurisée"""
        message_chiffre = self.chiffrer_message(message)
        socket_ssl.send(message_chiffre.encode())
        print(f"Message envoyé: {message_chiffre[:20]}...")
    
    def recevoir_message(self, socket_ssl):
        """Reçoit et déchiffre un message via la connexion sécurisée"""
        message_chiffre = socket_ssl.recv(1024).decode()
        message, authentique = self.dechiffrer_message(message_chiffre)
        
        if authentique:
            print(f"Message reçu: {message}")
            return message
        else:
            print("Message non authentique - potentiellement compromis")
            return ""

# Exemple d'utilisation (simulation)
protocole = ProtocoleSecurise()
# protocole.etablir_connexion_securisee("exemple.com", 443)
Lois et principes :

Loi de la confidentialité : Les données doivent être chiffrées pendant la transmission

Principe de l'authentification : Vérifier l'identité des parties

Synergie de la sécurité : SSL/TLS combine chiffrement et authentification

Cours bien détaillé
Sécurité = Chiffrement × Authentification × Intégrité × Confidentialité
Formule de la sécurité cryptographique
🎯
Définition : La cryptographie basique est l'ensemble des techniques permettant de protéger les informations par le chiffrement.
📏
Objectifs : Protéger la confidentialité, assurer l'intégrité, garantir l'authentification des données.
📐
Types : Chiffrement symétrique, asymétrique, hachage, signatures numériques.
📝
Applications : Communications sécurisées, transactions bancaires, stockage de données, identité numérique.
💡
Conseil : Utilisez des algorithmes éprouvés comme AES ou RSA
🔍
Attention : Ne jamais réinventer les algorithmes de sécurité
Astuce : Utilisez des clés longues pour plus de sécurité
📋
Méthode : Combine chiffrement et authentification
Vérification : Testez la sécurité avec des outils spécialisés
Types de cryptographie :
  • Symétrique : Même clé pour chiffrer et déchiffrer (AES, DES)
  • Asymétrique : Clés différentes pour chiffrer/déchiffrer (RSA, ECC)
  • Hachage : Transformation unidirectionnelle (SHA-256, MD5)
  • Hybride : Combinaison de plusieurs méthodes
  • Quantique : Cryptographie résistante aux ordinateurs quantiques
Concepts clés à retenir :
  • Le chiffrement protège les données contre l'accès non autorisé
  • Les clés symétriques sont plus rapides mais nécessitent un échange sécurisé
  • Les clés asymétriques permettent l'échange de clés sans canal sécurisé
  • Les algorithmes de hachage assurent l'intégrité des données
  • La sécurité dépend de la longueur des clés et de la complexité de l'algorithme
Évolution des techniques cryptographiques

Cryptographie classique → RSA → AES → Protocoles SSL/TLS → Cryptographie quantique

Cryptographie basique Principes de sécurité