Cryptographie asymétrique
Dans cette leçon, nous allons explorer la cryptographie à clé asymétrique, qui constitue la base de nombreuses interactions réseau sécurisées aujourd'hui.
À la fin de cette leçon, nous aurons abordé :
- Ce qu'est la cryptographie à clé asymétrique
- Les usages de la cryptographie à clé asymétrique, notamment l'échange de clés et les signatures numériques
- La sécurité de la cryptographie à clé asymétrique en général
- Des détails supplémentaires sur les algorithmes RSA, DSA et les courbes elliptiques, et leur sécurité
- Des exemples de code Python montrant comment ces algorithmes fonctionnent en pratique
- Les menaces pesant sur ces algorithmes, tant du côté des ordinateurs classiques que quantiques
Introduction à la cryptographie asymétrique
Comme nous l'avons vu dans la leçon précédente, la cryptographie à clé symétrique est très rapide et efficace pour protéger les informations, mais elle présente quelques limitations :
- À mesure que le nombre de parties souhaitant échanger des informations en toute sécurité augmente, le nombre de clés nécessaires croît de manière combinatoire. Elle ne fournit aucun mécanisme pour distribuer ces clés de façon sécurisée entre expéditeurs et destinataires.
- Il n'existe aucune disposition pour la non-répudiation. N'importe quelle partie peut déchiffrer ou chiffrer des messages sans moyen de garantir qu'un message a été reçu ou d'où il provient.
La solution à ces deux problèmes est apportée par la cryptographie à clé asymétrique (AKC), également connue sous le nom de cryptographie à clé publique (PKC), qui constitue ainsi une pierre angulaire de la sécurité numérique moderne.
La cryptographie à clé asymétrique (AKC) implique l'utilisation d'une paire de clés — une publique, une privée. Les clés publique et privée sont liées cryptographiquement et généralement créées au même moment en tant que paire de clés via un algorithme mathématique spécialisé. La clé publique, comme son nom l'indique, est destinée à être librement distribuée, tandis que la clé privée est gardée secrète par la partie qui génère la paire de clés. La sécurité des communications utilisant des paires de clés asymétriques est assurée tant que la clé privée reste confidentielle.

Figure 1. Chiffrement à clé asymétrique
L'AKC offre plusieurs fonctions utiles, telles que :
- Chiffrement et déchiffrement pour assurer la confidentialité des communications.
- Signatures numériques pour garantir l'authenticité, l'intégrité et la non-répudiation.
- Échange de clés sécurisé pour faciliter l'utilisation ultérieure de cryptosystèmes symétriques.
Dans les applications modernes, l'AKC est principalement utilisée pour les signatures numériques et l'échange de clés sécurisé. Dans cette leçon, nous présentons ces deux fonctions clés, puis nous abordons plusieurs variantes de protocoles cryptographiques pour ces fonctions.
Échange de clés avec la cryptographie asymétrique
L'un des problèmes fondamentaux en cryptographie est l'échange de clés sécurisé. Par exemple, si deux parties veulent utiliser le chiffrement symétrique, les deux parties ont besoin de la même clé pour chiffrer et déchiffrer les messages. Mais comment échangent-elles la clé en toute sécurité ? La cryptographie à clé asymétrique répond à cela via des mécanismes d'échange de clés interactifs et non interactifs.
Échange de clés interactif
Un protocole d'échange de clés interactif désigne une méthode par laquelle deux parties collaborent pour créer une clé secrète partagée sur un canal de communication non sécurisé. Cette clé secrète partagée peut ensuite être utilisée pour les tâches de chiffrement et de déchiffrement symétriques.
Le plus connu de ces protocoles est l'algorithme Diffie-Hellman (DH), conçu spécifiquement pour faciliter l'échange de clés. Dans ce protocole, chaque partie génère une paire de clés (publique et privée) et diffuse sa clé publique. Chaque partie utilise ensuite sa propre clé privée et la clé publique de l'autre partie pour générer une clé secrète partagée. DH s'appuie sur les principes de l'arithmétique modulaire pour garantir que les deux parties obtiennent le même secret partagé, même si chaque partie n'a accès qu'à la clé publique de l'autre.
Les cryptosystèmes modernes basés sur la cryptographie sur les courbes elliptiques (ECC) étendent ce concept avec l'échange de clés Diffie-Hellman sur courbe elliptique (ECDH). L'ECDH fonctionne de façon similaire au DH mais utilise les propriétés des courbes elliptiques, ce qui donne un système plus sécurisé et plus efficace.

Figure 2. Protocole d'échange de clés
Échange de clés non interactif
Contrairement aux protocoles d'échange de clés comme DH et ECDH, qui sont interactifs et nécessitent des échanges aller-retour pour décider de la clé symétrique, l'AKC offre aussi des moyens non interactifs d'établir une clé secrète partagée. Dans ces schémas, une partie génère une paire de clés (publique et privée) et partage la clé publique avec l'autre partie. Cette seconde partie génère alors une clé symétrique aléatoire, la chiffre avec la clé publique reçue et la renvoie à la première partie. La première partie utilise sa clé privée pour déchiffrer le message reçu, obtenant ainsi la clé symétrique partagée. Ce schéma est non interactif en ce sens que la clé symétrique est déterminée par une partie et simplement communiquée de manière sécurisée à l'autre partie sous forme chiffrée.
Une considération importante dans l'échange de clés non interactif concerne la différence de longueur en bits entre la clé symétrique que les parties souhaitent échanger et les tailles de messages recommandées en AKC. En général, les clés symétriques modernes ont une longueur comprise entre 128 et 256 bits, tandis que les cryptosystèmes à clé asymétrique comme RSA travaillent avec des tailles de messages d'environ 1024 à 4096 bits. Par conséquent, lors de l'utilisation de l'AKC pour transmettre une clé symétrique, celle-ci doit néanmoins être encodée dans un message plus long de 1024 à 4096 bits pour des raisons de sécurité. Cela peut être réalisé via deux approches :
-
Échange de clés basé sur le rembourrage : Dans cette approche, la clé symétrique plus courte (128-256 bits) est générée en premier, puis un schéma de rembourrage réversible convenu, tel que OAEP, est utilisé pour l'intégrer dans un message plus long (1024-4096 bits). Ce message plus long est chiffré via l'AKC et diffusé comme texte chiffré. Le destinataire déchiffre d'abord le texte chiffré, puis supprime le rembourrage pour extraire la clé symétrique plus courte.
-
Mécanismes d'encapsulation de clés (KEMs) : Avec l'échange de clés basé sur les KEM, un message aléatoire en clair long (1024-4096 bits) est d'abord généré, à partir duquel une clé symétrique plus courte (128-256 bits) peut être extraite via une fonction de dérivation de clé (KDF) convenue. Le texte en clair plus long est chiffré via l'AKC et diffusé au destinataire sous forme de texte chiffré. Le destinataire décode le texte chiffré à l'aide de sa clé privée, puis utilise la KDF pour extraire la clé symétrique plus courte (128-256 bits). Des cryptosystèmes populaires comme RSA, avec leur capacité à chiffrer directement des données, peuvent être utilisés pour implémenter des KEMs.

Figure 3. Mécanisme d'encapsulation de clé
Signatures numériques avec la cryptographie asymétrique
Les signatures numériques sont une autre application puissante de la cryptographie à clé asymétrique. Elles offrent l'authentification, l'intégrité et la non-répudiation, rendues possibles par le fait que dans l'AKC, les entités possèdent des clés privées uniques. L'idée de base des protocoles de signature est que les expéditeurs de messages sécurisés signent numériquement les messages en utilisant leur clé privée unique. Le destinataire vérifie ensuite la signature numérique à l'aide de la clé publique de l'expéditeur. Dans l'AKC, les signatures numériques peuvent être mises en œuvre à l'aide d'algorithmes spécifiquement conçus à cet effet ou à l'aide de cryptosystèmes génériques.

Figure 4. Signatures numériques avec la cryptographie asymétrique
Algorithmes de signature numérique dédiés
Actuellement, la norme de traitement de l'information fédérale américaine (FIPS) pour les signatures numériques est un schéma dédié simplement intitulé l'Algorithme de signature numérique (DSA). De façon assez similaire au protocole Diffie-Hellman, le DSA utilise les propriétés algébriques de l'exponentiation modulaire et des inverses multiplicatifs pour la génération et la vérification des signatures.
L'algorithme de signature numérique sur courbe elliptique (ECDSA) est une variante ECC du DSA, offrant les mêmes fonctionnalités mais avec des clés nettement plus courtes. Cela se traduit par une efficacité accrue, ce qui en fait un choix populaire pour les systèmes avec des contraintes de ressources.
DSA et ECDSA seront illustrés plus en détail par la suite.
Schémas de signature numérique utilisant des cryptosystèmes génériques
En plus des algorithmes dédiés, les signatures numériques peuvent également être générées à l'aide de cryptosystèmes asymétriques génériques, tels que RSA.
RSA, qui sera discuté en détail dans une section ultérieure, exploite également les inverses multiplicatifs modulaires et l'exponentiation modulaire comme opérations fondamentales, mais les combine dans une séquence différente de celle du DSA. Dans RSA, le signataire crée généralement un hachage du message puis chiffre ce hachage avec sa clé privée, créant ainsi la signature numérique. N'importe quelle partie peut alors vérifier cette signature en la déchiffrant avec la clé publique du signataire et en la comparant au message haché.
Applications de la cryptographie asymétrique
La cryptographie à clé asymétrique est omniprésente dans les applications technologiques numériques modernes. Les fonctionnalités de base de l'AKC décrites ci-dessus constituent les briques de construction de nombreux protocoles d'application de plus haut niveau, notamment :
-
Communication sur Internet : La communication sécurisée sur Internet, telle que HTTPS, repose fortement sur la cryptographie à clé asymétrique. Le protocole TLS (Transport Layer Security) et son prédécesseur, SSL (Secure Sockets Layer), utilisent la cryptographie à clé asymétrique lors du processus initial de poignée de main pour établir une clé symétrique, qui est ensuite utilisée pour le reste de la session de communication.
-
Authentification : La cryptographie à clé asymétrique est utilisée pour créer des signatures numériques, permettant à une entité d'authentifier un document ou un message numérique comme provenant d'un expéditeur spécifique. Cela est utilisé dans de nombreux scénarios, de la vérification des mises à jour logicielles aux contrats numériques légalement contraignants.
-
Chiffrement des e-mails : Les protocoles de chiffrement des e-mails tels que PGP (Pretty Good Privacy) et son alternative open source GPG (GNU Privacy Guard) utilisent la cryptographie à clé asymétrique pour garantir que seul le destinataire prévu peut lire le contenu de l'e-mail.
-
Shell sécurisé (SSH) : SSH est un protocole permettant la connexion à distance sécurisée et d'autres services réseau sécurisés sur un réseau non sécurisé. Il utilise la cryptographie à clé asymétrique pour authentifier le serveur auprès du client et, éventuellement, le client auprès du serveur.
-
VPN (réseau privé virtuel) : Le chiffrement à clé asymétrique est utilisé pour établir des connexions sécurisées dans les VPN, garantissant une communication sécurisée sur des réseaux publics.
-
Blockchain et cryptomonnaies : Les technologies blockchain, notamment Bitcoin et Ethereum, utilisent la cryptographie à clé asymétrique. Par exemple, la propriété des bitcoins est établie grâce à des signatures numériques utilisant la cryptographie à clé asymétrique.
-
Autorités de certification : La cryptographie à clé asymétrique est utilisée par les autorités de certification (CA) pour émettre et signer des certificats numériques, utilisés dans les communications TLS, la signature de code, le chiffrement des e-mails, et plus encore. Un certificat numérique associe une clé publique à une entité spécifique (par exemple, une personne ou un serveur).

Figure 5. Émission et signature de certificats numériques avec la cryptographie asymétrique
Sécurité de la cryptographie asymétrique
Plusieurs notions cryptographiques se combinent pour permettre une cryptographie à clé asymétrique sécurisée, notamment :
Confidentialité de la clé privée : L'exigence de sécurité la plus fondamentale de l'AKC est que la clé privée reste secrète. Cependant, puisque la clé privée doit être mathématiquement liée à la clé publique, la confidentialité de la clé privée exige également qu'il soit computationnellement infaisable de dériver la clé privée à partir de la connaissance de la clé publique. Les schémas de génération de clés dans l'AKC s'appuient sur des problèmes mathématiques computationnellement difficiles pour faciliter cette exigence.
Fonctionnalité à trappe Dans l'AKC, les opérations de chiffrement et de déchiffrement impliquent différentes clés complémentaires d'une même paire de clés. Le texte chiffré généré par le chiffrement à l'aide de l'une des clés (par exemple la clé publique) doit être impénétrable pour les tiers, tout en étant facilement déchiffrable par le détenteur de la clé complémentaire (dans ce cas la clé privée). Autrement dit, le chiffrement doit ressembler à une fonction à sens unique avec trappe telle que les tiers ne puissent pas inverser l'opération et récupérer le texte en clair, mais que la clé privée fournisse une trappe secrète permettant une inversion facile. Les algorithmes AKC populaires utilisent l'exponentiation modulaire pour mettre en place un comportement de fonction à sens unique avec trappe.
Aléatoire : Le processus de génération de clés doit également exploiter l'aléatoire pour garantir que les clés sont imprévisibles, car tout motif ou toute prévisibilité dans la génération de clés pourrait être exploité par un attaquant. L'aléatoire est également utilisé pour le rembourrage lors du chiffrement afin de générer des textes chiffrés sémantiquement sécurisés et dans les schémas de signature numérique pour produire des signatures uniques, même lorsque le même message est signé plusieurs fois. Pour cette raison, l'utilisation de générateurs de nombres aléatoires forts est un aspect important de l'AKC.
Grande taille de clé : Comme dans le cas du SKC, de grandes tailles de clés assurent une protection contre les attaques par force brute. Cependant, comme de grandes tailles de clés augmentent également le coût computationnel du processus de chiffrement et de déchiffrement, une solution optimale doit équilibrer les considérations de sécurité et d'efficacité. Le tableau suivant montre les tailles de clés typiques pour divers protocoles et applications de cryptographie à clé asymétrique :
| Protocole | Tailles de clés typiques (en bits) | Application |
|---|---|---|
| RSA | 1024 (obsolète), 2048, 3072, 4096 | Chiffrement, signatures numériques |
| DSA | 1024 (obsolète), 2048, 3072 | Signatures numériques |
| DH | 2048, 3072, 4096 | Échange de clés |
| ECDH | 224, 256, 384, 521 | Échange de clés |
| ECDSA | 224, 256, 384, 521 | Signatures numériques |
Infrastructure à clé publique : Dans l'AKC, les clés privées doivent être gardées secrètes par leurs propriétaires tandis que les clés publiques sont partagées. Il doit exister un mécanisme sécurisé pour gérer et distribuer ces clés publiques entre les utilisateurs. L'infrastructure à clé publique (PKI) fournit un moyen de le faire en utilisant des certificats numériques. Un certificat numérique fournit une preuve d'identité du propriétaire de la clé publique et est émis par une autorité de confiance comme une autorité de certification (qui fait partie d'une PKI). Ainsi, la PKI joue un rôle intégral dans la sécurité des applications modernes utilisant l'AKC en permettant une gestion des clés à grande échelle (en créant, gérant, distribuant et révoquant des certificats numériques).
Risques de sécurité pour la cryptographie asymétrique
Comme indiqué dans le tableau ci-dessus, les algorithmes à clé asymétrique modernes tels que RSA emploient généralement des tailles de clés beaucoup plus grandes que leurs homologues à clé symétrique couramment utilisés, comme AES-128. Même les protocoles basés sur ECC (ECDH et ECDSA) qui ont des tailles de clés plus petites emploient un minimum d'au moins 224 bits pour les clés. Cela implique à son tour qu'une attaque par force brute impliquant une recherche exhaustive de l'espace de clés privées pour identifier la clé correcte est computationnellement intraitable dans un avenir prévisible. Cela reste vrai même si des ordinateurs quantiques étaient déployés pour cette tâche. Par conséquent, les attaques contre l'AKC se concentrent généralement sur l'exploitation d'autres faiblesses potentielles de cryptosystèmes spécifiques. Certains modes d'attaque bien documentés ciblent :
-
La faiblesse algorithmique en utilisant des moyens mathématiques et computationnels sophistiqués pour saper les hypothèses de difficulté utilisées pour formuler les algorithmes à clé asymétrique. Par exemple, la sécurité de RSA repose sur la difficulté de factoriser de grands nombres premiers, et des avancées computationnelles récentes ont permis la factorisation réussie de clés RSA de 829 bits. Par conséquent, RSA 1024 bits est actuellement obsolète. Comme cela sera discuté plus tard, le principal risque posé par les ordinateurs quantiques à l'AKC entre également dans cette catégorie.
-
L'aléatoire imparfait, qui peut conduire à une faiblesse dans le processus de génération de clés. Par exemple, si le générateur de nombres aléatoires utilisé pour créer les clés est défectueux et génère des clés qui ne sont pas vraiment aléatoires, un attaquant pourrait être en mesure de prédire les clés.
-
Les failles d'implémentation telles que des erreurs dans l'implémentation des algorithmes cryptographiques qui révèlent par inadvertance des informations sur les clés. Par exemple, un rembourrage incorrect peut potentiellement révéler des détails sur les clés.
-
Les canaux auxiliaires qui désignent la fuite d'informations depuis le système physique qui effectue la cryptographie. Ces fuites pourraient prendre la forme d'informations de synchronisation, de consommation d'énergie, ou même de son, qui peuvent être exploitées dans ce qu'on appelle une attaque par canal auxiliaire. Par exemple, analyser la durée des opérations cryptographiques pourrait révéler le nombre de '1' dans une clé binaire. Cette fuite apparemment innocente réduit considérablement l'espace de recherche, dévoilant des solutions potentielles à des problèmes qui semblaient initialement insurmontables.
-
L'échange de clés en interceptant les clés pendant leur échange, comme dans une attaque de l'homme du milieu (MITM). Le protocole DH est susceptible aux attaques MITM si des étapes d'authentification supplémentaires ne sont pas incorporées.
-
Le stockage des clés en visant à voler les clés depuis des stockages mal sécurisés. Cela inclut les attaques physiques telles que la manipulation ou le vol de dispositifs de stockage.
Sécuriser les cryptosystèmes à clé asymétrique contre la variété des attaques possibles est donc une tâche importante impliquant des considérations mathématiques, matérielles, logicielles, logistiques et juridiques.
RSA
L'algorithme RSA (Rivest-Shamir-Adleman) est l'un des premiers cryptosystèmes à clé publique et est largement utilisé pour la transmission sécurisée de données. C'est un cryptosystème polyvalent en ce sens qu'il fournit les opérations nécessaires pour permettre le chiffrement, le déchiffrement, les signatures numériques et l'échange de clés dans un cadre commun.
Dans cette section, nous allons illustrer la cryptographie à clé asymétrique (AKC) en utilisant RSA à travers des exemples simples.
Nous utiliserons le scénario standard de deux parties, Alice et Bob, qui souhaitent communiquer de manière sécurisée à l'aide de l'AKC.
L'algorithme RSA
L'algorithme RSA de base implique quatre opérations : la génération de clés, la distribution de clés, le chiffrement et le déchiffrement :
-
Génération de clés :
Les clés publique et privée sont générées sur la base de principes mathématiques autour des nombres premiers, où les calculer est facile, mais l'inverse est difficile.
Nous allons y faire référence ainsi :
- Clé publique :
- Clé privée :
Remarque : est commun à la clé publique et à la clé privée, et est connu comme le modulus. Nous devrons l'utiliser plus tard.
Détail mathématique
- Choisis deux nombres premiers distincts, et .
- choisis aléatoirement (pour la sécurité).
- Ils doivent être de grandeurs similaires, mais différer en longueur de quelques chiffres, pour rendre la factorisation plus difficile.
- Les nombres premiers peuvent être choisis efficacement à l'aide d'un test de primalité.
- Calcule .
- est le modulus pour les deux clés publique et privée.
- Calcule la fonction indicatrice d'Euler .
- L'indicatrice est censée rester secrète et est généralement supprimée après la génération de clés.
- Choisis un entier tel que et .
- c'est-à-dire que et doivent être coprimes.
- Ce nombre constitue l'exposant de la clé publique et est généralement choisi comme un petit nombre pour l'efficacité computationnelle.
- Le nombre premier est souvent utilisé.
- Calcule pour satisfaire la relation de congruence .
- C'est-à-dire que est l'inverse multiplicatif de modulo .
- Cela est plus efficacement calculé à l'aide de l'algorithme euclidien étendu.
- Ce nombre est l'exposant de la clé privée.
- La clé publique est constituée de , et la clé privée est .
-
Distribution de clés :
- La clé publique est rendue publique à ceux qui souhaitent envoyer un message
- La clé privée est gardée secrète.
-
Chiffrement :
- Alice souhaite envoyer un message à Bob. Dans ce cas, un simple entier
- Alice utilise la clé publique de Bob pour chiffrer le message en texte chiffré
Détail mathématique
- est un entier .
- , où est le texte chiffré.
-
Déchiffrement :
- Bob reçoit le texte chiffré
- Bob utilise sa clé privée pour déchiffrer le message en message
Détail mathématique
- .
Voici l'esquisse de base de RSA. En pratique, des schémas de rembourrage plus sophistiqués sont appliqués au texte en clair avant le chiffrement pour garantir que des textes en clair identiques donnent des textes chiffrés différents. Cela prévient une gamme d'attaques possibles contre les implémentations naïves de RSA et permet la sécurité sémantique.
Illustration de RSA en Python
Dans les cellules de code suivantes, nous illustrons un exemple simple de l'algorithme RSA en utilisant de petits entiers, puis nous démontrons des applications pratiques de distribution de clés et de signature numérique en utilisant des bibliothèques Python implémentant RSA.
Remarque : Dans cette section, nous montrerons les calculs mathématiques en détail dans le cadre du code Python
Génération de clés RSA
Parcourons pas à pas une instance simple de l'algorithme RSA en employant de petits nombres premiers.
Nous devrons être en mesure de calculer le plus grand commun diviseur de deux entiers, car il sera nécessaire pour tester si deux entiers sont coprimes.
Nous expliquerons une façon simple de calculer cela, mais il est beaucoup plus efficace avec des entiers plus grands d'utiliser la fonction Python math.gcd.
# Added by doQumentation — required packages for this notebook
!pip install -q cryptography
import math
# Example function to compute the gcd (greatest common divisor)
def gcd(a, b):
if b == 0:
return a
else:
return gcd(b, a % b)
# let's calculate some examples using algorithm
n1 = gcd(50, 10)
n2 = gcd(99, 33)
n3 = gcd(59, 9)
# do the same with the python library call
m1 = math.gcd(50, 10)
m2 = math.gcd(99, 33)
m3 = math.gcd(59, 9)
# Confirm they are the same
assert n1 == m1
assert n2 == m2
assert n3 == m3
# They are - print out the values for explanation
print("gcd(50,10) =", m1)
print("gcd(99,33) =", m2)
print("gcd(59,9) =", m3)
La première phase du flux de travail RSA est la génération de clés. Elle est initiée en choisissant deux nombres premiers, qui sont censés être gardés secrets par l'entité générant les clés.
# Choosing two prime numbers and keep them secret
p = 13
q = 19
print("The secret prime numbers p and q are:", p, q)
Ensuite, le modulus , qui est simplement le produit des deux premiers choisis, est calculé. Ce modulus sera publié dans le cadre de la clé publique.
# Calculate n which is the modulus for both the public and private keys
n = p * q
print("modulus n (p*q)=", n)
La fonction indicatrice d'Euler est calculée ensuite, car elle est nécessaire pour l'opération d'inverse multiplicatif modulaire utilisée pour déterminer les clés dans RSA. est également gardé secret et généralement supprimé après la génération de clés.
# Compute Euler's totient function, φ(n) and keep it secret
phi = (p - 1) * (q - 1)
print("The secret Euler's function (totient) [phi(n)]:", phi)
Nous sommes maintenant prêts à calculer les clés publique et privée. Dans RSA, chacune d'elles est spécifiée par un tuple de deux entiers. La première entrée dans chaque tuple est un entier distinct, et la deuxième entrée est le modulus commun aux deux clés.
La première entrée dans la clé publique peut être n'importe quel entier supérieur à 1 qui est coprime à . Deux entiers sont coprimes si leur plus grand commun diviseur est 1. Nous utilisons donc la fonction math.gcd pour trouver un entier coprime à .
# Choose an integer e such that e and φ(n) are coprime
e = 2
while e < phi:
if math.gcd(e, phi) == 1:
break
else:
e += 1
print("Public Key (e):", e)
La clé privée nécessite un entier , qui est l'inverse multiplicatif de modulo ; c'est-à-dire qu'il satisfait la relation de congruence . Pour cette illustration simple où nous traitons de petits entiers, nous pouvons simplement parcourir les entiers positifs pour localiser un adéquat. Dans des contextes réalistes, l'algorithme euclidien étendu, computationnellement efficace, est utilisé à cet effet.
# Compute a value for d such that (d * e) % φ(n) = 1
d = 1
while True:
if (d * e) % phi == 1:
break
else:
d += 1
print("Private Key (d):", d)
Nous formons maintenant les tuples , qui constituent respectivement les clés publique et privée. La clé publique est ensuite publiée, tandis que la clé privée est gardée secrète.
# Public and Private Key pair
public = (e, n)
private = (d, n)
print(f"The Public key is {public} and Private Key is {private}")
Le chiffrement et le déchiffrement dans RSA utilisent l'opération d'exponentiation modulaire. De plus, les clés publique et privée sont complémentaires en ce sens que l'une ou l'autre peut être utilisée pour chiffrer un message que l'autre peut ensuite déchiffrer.
Nous illustrons ici le cas où la clé publique est utilisée pour le chiffrement et la clé privée est utilisée pour le déchiffrement, en définissant une fonction Python pour chaque opération.
Nous chiffrons ensuite et déchiffrons un message entier .
# Encryption function
def encrypt(plain_text):
return (plain_text**e) % n
# Decryption function
def decrypt(cipher_text):
return (cipher_text**d) % n
# Simple message to encode
msg = 9
# encrypt then decrypt
enc_msg = encrypt(msg)
dec_msg = decrypt(enc_msg)
print("Original Message:", msg)
print("Encrypted Message:", enc_msg)
print("Decrypted Message:", dec_msg)
Bien que les petits entiers utilisés ci-dessus soient utiles pour exposer facilement les idées fondamentales de l'algorithme RSA, dans les applications réelles RSA nécessite l'utilisation de très grands entiers. Par exemple, RSA 2048 bits implique l'utilisation d'un modulus long de 2048 bits, dont les équivalents entiers décimaux sont d'environ 10. Ces nombres vraiment énormes sont nécessaires pour la sécurité pratique de RSA.
Échange de clés symétrique avec RSA
Comme discuté précédemment, l'AKC permet à deux parties souhaitant communiquer d'établir de manière sécurisée un secret partagé, qui peut être utilisé, par exemple, comme clé secrète pour le chiffrement symétrique ultérieur de texte en clair en masse.
Considérons le scénario suivant. Alice et Bob veulent utiliser le SKC pour chiffrer et déchiffrer des messages. Cependant, avant que ce processus puisse être initialisé, ils doivent s'accorder sur une clé secrète commune. Une option est que l'une des parties — disons Alice — génère une clé secrète et la transmette ensuite de manière sécurisée à Bob. Pour réaliser ce transfert sécurisé, Alice décide d'utiliser RSA comme mécanisme d'encapsulation de clé (KEM).
Cela implique les étapes suivantes :
- D'abord, Alice génère une clé symétrique aléatoire, qu'elle a l'intention de partager avec Bob.
- Ensuite, Bob génère une paire de clés asymétrique et rend sa clé publique disponible sur un canal approprié.
- Puis, Alice utilise la clé publique de Bob pour chiffrer la clé symétrique, l'encapsulant ainsi dans un texte chiffré.
- Ensuite, Alice diffuse le texte chiffré sur un canal fiable mais pas nécessairement sécurisé.
- Enfin, Bob reçoit le texte chiffré et le déchiffre à l'aide de sa clé privée. Il a maintenant accès à la clé symétrique générée par Alice.

Figure 1. Échange de clés symétrique avec RSA
Exemple d'échange de clés basé sur le rembourrage en Python
Un flux de travail pratique utilisant RSA pour l'échange de clés non interactif basé sur le rembourrage est maintenant illustré à l'aide de la bibliothèque Python cryptography.
Importe les modules nécessaires depuis la bibliothèque Python cryptography. Si nécessaire, cette bibliothèque peut être installée à l'aide de la commande pip install cryptography.
Alice génère ensuite une clé secrète aléatoire, qu'elle a l'intention de transmettre à Bob.
# pip install cryptography
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
symmetric_key = Fernet.generate_key()
print(f"\nSymmetric key generated by Alice: {symmetric_key}")
En utilisant le module rsa de la bibliothèque cryptography, Bob génère une paire de clés et diffuse ensuite sa clé publique. N'importe qui peut intercepter la clé publique et lire les nombres publics qui la composent.
# Bob generates a 2048-bit RSA key pair
bob_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
bob_public_key = bob_private_key.public_key()
print(f"Public key broadcast by Bob: {bob_public_key}")
print(f"\nPublic numbers in Bobs' public key: {bob_public_key.public_numbers()}")
À ce stade, on suppose qu'Alice a reçu la clé publique diffusée par Bob. La symmetric_key d'Alice peut maintenant être chiffrée à l'aide de la clé publique de Bob pour produire le texte chiffré. Dans un contexte réaliste, Alice utilisera également des méthodes de rembourrage supplémentaires telles que OAEP pour assurer la sécurité sémantique de ses communications avec Bob.
# Encryption
ciphertext = bob_public_key.encrypt(
symmetric_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Ciphertext:", ciphertext)
Alice diffuse ensuite le texte chiffré sur un canal ouvert, confiante que seul Bob, disposant de la clé privée correspondante, pourra le déchiffrer. On suppose que Bob a reçu le texte chiffré et peut maintenant le déchiffrer à l'aide de sa clé privée confidentielle.
# Bob decrypts ciphertext to access the symmetric key
decrypted_symmetric_key = bob_private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Decrypted key:", decrypted_symmetric_key)
assert decrypted_symmetric_key == symmetric_key
À ce stade, Alice et Bob ont tous les deux accès à la clé symétrique secrète, qu'ils peuvent utiliser pour des applications de chiffrement à clé symétrique (SKC).
Simuler un mécanisme d'encapsulation de clé avec RSA en Python
Dans le flux de travail suivant, on illustre l'utilisation de RSA pour simuler un mécanisme d'encapsulation de clé (KEM), par lequel un message secret aléatoire suffisamment long est échangé de manière sécurisée puis converti en un secret partagé de la longueur appropriée à l'aide d'une KDF.
Une fois encore, Alice et Bob souhaitent établir un secret partagé de façon non interactive, et c'est Alice qui décide quel secret utiliser.
On commence par importer quelques bibliothèques Python nécessaires.
Bob génère ensuite sa paire de clés RSA et diffuse sa clé publique.
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from os import urandom
# Bob's RSA key pair
private_key_Bob = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key_Bob = private_key_Bob.public_key()
print("Bob's private and public keys created")
Alice génère d'abord un long secret aléatoire à partir duquel le secret partagé sera finalement dérivé. Dans un KEM pur, le long secret est un élément aléatoire de la structure algébrique sous-jacente au cryptosystème. Dans le cas de RSA 2048 bits, ce serait un entier aléatoire modulo le module RSA de 2048 bits. Un tel KEM pur ne nécessite pas de rembourrage supplémentaire, mais dans cet exemple, on simule seulement un KEM à l'aide de RSA et la bibliothèque cryptography exige l'utilisation d'un rembourrage lors du chiffrement avec RSA. On utilisera donc un long secret un peu plus court, qui reste néanmoins bien plus long qu'une clé AES standard de 256 bits.
Alice_long_secret = urandom(160) # A 160 byte or 1280 bit random message
print("Alice's secret created")
Alice chiffre ensuite le long secret à l'aide de la clé publique de Bob, et le secret chiffré est communiqué à Bob.
Alice_encrypted_secret = public_key_Bob.encrypt(
Alice_long_secret,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Alice's secret encrypted")
Bob déchiffre le secret chiffré reçu d'Alice à l'aide de sa clé privée.
Bob_decrypted_secret = private_key_Bob.decrypt(
Alice_encrypted_secret,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
assert Alice_long_secret == Bob_decrypted_secret, "Secrets do not match!"
# if we get here they match
print("Secrets match")
Enfin, Alice et Bob appliquent séparément une fonction de dérivation de clé (KDF) convenue sur le long secret pour dériver la clé symétrique.
Il est à noter que le processus implique un protocole de hachage et l'utilisation d'un sel aléatoire, ce qui garantit l'unicité et l'imprévisibilité de la clé symétrique partagée au cas où le long secret serait réutilisé (ce qui n'est pas recommandé). Cependant, le sel lui-même n'a pas besoin d'être secret, et une fois qu'il est généré aléatoirement — par Alice dans cet exemple —, il peut être diffusé à Bob en même temps que le long secret chiffré.
On suppose donc qu'Alice et Bob ont tous les deux accès au même sel aléatoire.
def key_derivation_function(secret, salt):
hkdf = HKDF(
algorithm=hashes.SHA256(),
length=32, # Desired key length
salt=salt,
info=None,
backend=None,
)
return hkdf.derive(secret)
common_salt = urandom(16) # Random salt accessible to both Alice and Bob
symmetric_key_Alice = key_derivation_function(Alice_long_secret, common_salt)
symmetric_key_Bob = key_derivation_function(Bob_decrypted_secret, common_salt)
assert symmetric_key_Alice == symmetric_key_Bob, "Derived keys do not match!"
print(
f"A symmetric key of length {len(symmetric_key_Alice)*8} bits was successfully derived by both Alice and Bob!"
)
Signatures numériques avec RSA
On va maintenant étendre le scénario de communication confidentielle entre Alice et Bob pour y inclure la validation à l'aide d'une signature numérique.
Comme précédemment, Alice enverra confidentiellement un message encapsulant une clé symétrique à Bob, mais elle signera également le message numériquement pour que Bob, à la réception, puisse vérifier que c'est bien Alice qui en est l'auteure et que le contenu n'a pas été altéré pendant la transmission.
Plus généralement, il est souhaitable de permettre la validation sans compromettre la confidentialité, de sorte que toute partie intéressée soit en mesure de vérifier l'intégrité, l'authenticité, et d'établir la non-répudiation d'une communication donnée, même si cette partie n'a pas accès au message en clair.
On va considérer ce cadre général, qui implique alors les étapes suivantes :
- D'abord, Bob et Alice rendent leurs clés publiques disponibles sur un canal ouvert.
- Ensuite, Alice chiffre le texte en clair à l'aide de la clé publique de Bob, créant ainsi un texte chiffré.
- Alice crée ensuite un hachage du texte chiffré avec une fonction de hachage, puis chiffre le hachage résultant à l'aide de sa clé privée. Ce hachage chiffré est la signature.
- Alice transmet alors le texte chiffré et la signature sur un canal ouvert.
- Bob utilise la clé publique d'Alice pour déchiffrer la signature, révélant ainsi le hachage du texte chiffré.
- Puisque Bob a également accès au texte chiffré lui-même, il utilise la même fonction de hachage qu'Alice pour recréer une deuxième instance du hachage. Si celle-ci correspond à celle obtenue en déchiffrant la signature d'Alice, alors le message est validé, même si le texte chiffré lui-même n'a pas encore été déchiffré.
- Enfin, Bob, ayant validé le message, déchiffre le texte chiffré à l'aide de sa propre clé privée.

Figure 2. Signatures numériques avec RSA
Ce flux de travail pour une signature numérique est illustré ci-après.
On importe à nouveau quelques modules utiles de la bibliothèque cryptography.
Comme précédemment, Alice souhaite envoyer de manière sécurisée une clé symétrique à Bob, mais elle veut également la signer numériquement. Dans ce cas, on a besoin des clés publiques d'Alice et de Bob. La première étape consiste donc pour les deux à créer leur propre paire de clés RSA et à diffuser leur propre clé publique au monde entier.
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
# Generate keys for Bob
bob_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
bob_public_key = bob_private_key.public_key()
# Generate keys for Alice
alice_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
alice_public_key = alice_private_key.public_key()
print("Private and Public keys generated for Bob and Alice.")
À l'étape suivante, comme précédemment, Alice utilise la clé publique de Bob pour chiffrer la clé symétrique et prépare le texte chiffré.
# Alice encrypts the message using Bob's public key
ciphertext = bob_public_key.encrypt(
symmetric_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("ciphertext of symmetric key: ", ciphertext)
À ce stade, au lieu de simplement diffuser le texte chiffré, Alice souhaite y joindre une signature numérique pour pouvoir prouver à Bob qu'elle est bien l'expéditrice du message. Cela se fait en deux étapes :
- Créer un hachage du texte chiffré à l'aide d'un algorithme de hachage.
- Chiffrer le hachage à l'aide de la clé privée d'Alice, ce qui constitue la signature.
# Alice signs the ciphertext using her private key
digest = hashes.Hash(hashes.SHA256())
digest.update(ciphertext)
hash_to_sign = digest.finalize()
signature = alice_private_key.sign(
hash_to_sign,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
Prehashed(hashes.SHA256()),
)
print("signature: ", signature)
Alice diffuse ensuite le texte chiffré et la signature sur un réseau pour que Bob puisse les intercepter tous les deux.
# Bob receives the ciphertext and signature
received_ciphertext = ciphertext
received_signature = signature
# Send signature and ciphertext here
print("Sending ciphertext and signature.....")
Du côté de Bob, la première tâche consiste à vérifier l'intégrité et l'authenticité du texte chiffré. Pour ce faire, Bob crée un hachage du texte chiffré reçu en utilisant le même algorithme de hachage qu'Alice.
# Bob creates a hash of the ciphertext using the same algorithm used by Alice
digest = hashes.Hash(hashes.SHA256())
digest.update(received_ciphertext)
hash_to_verify = digest.finalize()
print("hash to verify: ", hash_to_verify)
Ensuite, Bob déchiffre la signature reçue à l'aide de la clé publique d'Alice. Puisqu'Alice a utilisé sa clé privée pour créer la signature, Bob peut la déchiffrer avec la clé publique d'Alice. La signature déchiffrée n'est rien d'autre qu'un hachage du texte chiffré créé du côté d'Alice. Si le hachage créé par Bob correspond à la signature déchiffrée, alors Bob a vérifié que le texte chiffré qu'il a reçu n'a pas été altéré et que c'est bien Alice qui l'a signé.
Dans le code Python ci-dessous, ces opérations sont regroupées dans une fonction utilitaire appelée verify fournie par un objet associé à la clé publique d'Alice.
from cryptography.exceptions import InvalidSignature
def is_signature_valid(public_key, signature, data_hash):
try:
public_key.verify(
signature,
data_hash,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH
),
Prehashed(hashes.SHA256()),
)
return True
except InvalidSignature:
return False
if is_signature_valid(alice_public_key, received_signature, hash_to_verify):
print("The signature is valid.")
else:
print("The signature is not valid.")
Ayant vérifié l'intégrité et l'authenticité du texte chiffré reçu, Bob peut alors le déchiffrer à l'aide de sa clé privée, car Alice a créé le texte chiffré en utilisant la clé publique de Bob.
# Bob decrypts the message using his private key
decrypted_message = bob_private_key.decrypt(
received_ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Decrypted message:", decrypted_message.decode())
Dans le scénario de signature numérique ci-dessus, n'importe quelle partie — pas seulement Bob — peut vérifier qu'Alice est l'expéditrice du texte chiffré, car tout le monde peut accéder à la clé publique d'Alice, au texte chiffré et à la signature numérique. De plus, après avoir envoyé le texte chiffré et la signature, Alice ne peut pas nier ultérieurement l'avoir fait, car la signature ne peut être déchiffrée en un hachage significatif qu'en utilisant sa clé publique. Cela établit la non-répudiation.
En permettant une distribution sécurisée des clés et en prenant en charge la non-répudiation, la cryptographie à clé publique établit une base solide pour une communication numérique sécurisée.
Casser RSA
L'utilité et la sécurité de l'algorithme RSA décrit ci-dessus reposent sur deux hypothèses mathématiques :
-
Trouver l'inverse multiplicatif modulaire en n'ayant accès qu'à est informatiquement infaisable.
-
Dans le contexte RSA, l'opération d'exponentiation modulaire se comporte comme une fonction à sens unique avec trappe. Utilisée pour le chiffrement, elle produit un texte chiffré indéchiffrable, et sans accès à la clé privée, inverser l'opération pour retrouver le texte en clair à partir du texte chiffré n'est pas faisable. En revanche, avec la clé privée, qui joue le rôle de trappe, le texte chiffré peut être facilement déchiffré.
L'attaque la plus connue contre l'algorithme RSA vise à contourner l'hypothèse 1 en récupérant efficacement le nombre de clé privée via la factorisation du modulus . Comme illustré ci-dessous, il est facile de retrouver si on a accès aux facteurs premiers et de ou à l'indicatrice d'Euler . Rappelons que , et sont gardés secrets lors de la génération des clés et supprimés ensuite. Un tiers qui récupère ces informations à l'aide d'un ordinateur classique ou quantique obtient essentiellement la clé privée, ce qui casse RSA. La factorisation en nombres premiers est donc la primitive calculatoire clé nécessaire pour casser RSA.
Informatique classique et RSA
La factorisation en nombres premiers de grands entiers est connue pour présenter une complexité super-polynomiale ou sous-exponentielle sur les ordinateurs classiques. Le meilleur algorithme classique connu pour factoriser des entiers supérieurs à est le crible algébrique (GNFS)