RSA: Der unknackbare Code?
Übersicht
Auf einen Blick
Schlüsselbegriffe: Verschlüsselung, Entschlüsselung, Modulo-Arithmetik, Klartext, Chiffretext, modulare Potenzen, Kryptographie
Alter: 16–18 Jahre
Vorkenntnisse/Fertigkeiten: Basiswissen in Python
Zeitumfang: 60 Minuten
Autor: Sam Robbins (UK)
Unterrichtsplan
| Zeit in Minuten | Unterrichtsphase | Aktivität | Material |
|---|---|---|---|
| 0–5 | Aufgabe 1 | Einführung in Fermats kleinen Satz | Arbeitsblatt, Taschenrechner |
| 5–15 | Aufgabe 2 | Die Lehrkraft erklärt modulare Potenzen durch wiederholte Multiplikation innerhalb der Modulo-Arithmetik. Schüler*innen implementieren den Programmcode und testen ihn anhand der Beispiele. | Arbeitsblatt, Python Interpreter |
| 15–20 | Einführung in das RSA-Verfahren am Beispiel | Lehrkraft erklärt Verschlüsselung und Entschlüsselung in RSA an dem Beispiel auf dem AB. | Arbeitsblatt |
| 20–40 | Aufgabe 3 | Schüler*innen übernehmen den Code und testen die Ver- und Entschlüsselung in RSA an weiteren Beispielen. | Arbeitsblatt, Python Interpreter |
| 40–60 | Aufgabe 4 | Schüler*innen entwerfen ein Programm zum Faktorisieren einer Zahl und messen die benötigte Zeit. | Arbeitsblatt, Python Interpreter |
Aufgaben und Hinweise für Lehrkräfte
In dieser Lektion beschäftigen wir uns mit dem RSA-Verschlüsselungsalgorithmus und klassischen Methoden, ihn zu brechen. Der RSA-Algorithmus wurde 1979 von Rivest, Shamir und Adleman entwickelt und ist bis heute eine der erfolgreichsten Formen der Verschlüsselung. Wenn du jemals eine Finanztransaktion im Internet durchgeführt hast, ist es sehr wahrscheinlich, dass die dabei verwendeten Sicherheitscodes mithilfe des RSA-Systems verschlüsselt und deine Identität darüber verifiziert wurde.
Diese Lektion untersucht die Methodik des RSA-Verfahrens und zeigt, warum es in der klassischen Informatik praktisch unknackbar ist.
Arbeitsblatt
Die folgenden Aufgaben können Sie als Arbeitsblatt in pdf oder docx herunterladen.
Die Aufgaben finden Sie hier auch als interaktives Arbeitsblatt mit einer eingebetteten Python-Umgebung.
Fermats kleiner Satz
Im Zentrum des RSA-Verfahrens steht ein schönes mathematisches Resultat, bekannt als Fermats kleiner Satz:
Das bedeutet: Wenn man eine natürliche Zahl mit einer um eins verminderten Primzahl (also mit ) potenziert, dann ergibt der Rest bei Division durch immer 1. Die mod-Funktion („modulo“) gibt den Rest nach der Division an.
Zum Beispiel nehmen wir und :
Aufgabe 1
Überprüfe Fermats kleinen Satz für folgende Werte von und :
Die Lehrkraft sollte zunächst demonstrieren, wie Modulo-Arithmetik funktioniert, z. B. Rest bei Division verschiedener Zahlen durch eine feste Zahl bestimmen.
Bei jeder Teilaufgabe ergibt sich 1.
Hinweis zur Implementierung
Das letzte Beispiel in Übung 1 zeigt ein potenzielles Problem bei der Implementierung von RSA: Die Potenzfunktion wächst extrem schnell. Selbst mit kleinen Primzahlen kann es schnell zu Überlauf-Fehlern kommen, wenn man die Potenz direkt berechnet. Das lässt sich vermeiden, indem man ausnutzt, dass eine Potenz denselben Rest besitzt, egal ob man als Basis die Zahl oder das Ergebnis verwendet. Man kann also Zwischenergebnisse bei jeder Multiplikation modulo p reduzieren.
Nehmen wir zum Beispiel wieder und , sehen wir:
Aber:
Statt mit der Modulo-Operation zu berechnen, können wir verwenden und finden wie gewünscht
.
Daher können wir zur Berechnung der modularen Potenz in Python eine Schleife schreiben, die x mehrfach mit sich selbst multipliziert und dabei jedes Mal den Rest modulo p berechnet.
Aufgabe 2
Implementiere den folgenden Python Code (% ist in Python die Notation für die Modulo-Operation):
def powerMod(a, b, c): # raises a to the power of b modulo c res = a % c for i in range(0, b - 1): res = a * res % c return res x = 5 p = 11 print( powerMod(x, p - 1, p) )
Nutze den Code, um herauszufinden, welche der folgenden Zahlen keine Primzahlen sind (weil Fermats kleiner Satz für sie nicht gilt):
- 1367
- 1369
- 7527
- 7531
- 7537
- 15723
- 15727
- 18791
Die Lehrkraft demonstriert das Potenzieren durch wiederholte Multiplikation einer Zahl mit sich selbst. Dabei wird nach jedem Schritt das Ergebnis modulo gerechnet. Ein Beispiel ist im Arbeitsblatt enthalten.
Die Schüler*innen implementieren den Code vom Arbeitsblatt (% ist der Python-Befehl für MOD). Sie können jeden Wert von x kleiner als p ausprobieren (x=2 wäre hier ausreichend). Ergibt sich für einen Wert von x eine andere Ausgabe als 1, ist p keine Primzahl.
Antworten: (b), (c), (d), (f) und (h) sind keine Primzahlen.
Der RSA-Algorithmus
Hinweis
Der nächste Abschnitt erklärt die Mathematik hinter RSA Schritt für Schritt. Dieser Abschnitt kann übersprungen werden, falls der Fokus stärker auf dem Anwenden statt dem Begründen liegen soll.
Die Zusammenfassungstabelle am Ende dieses Abschnitts enthält alle notwendigen Informationen zur Implementierung des Systems.
RSA beruht auf einer Variante von Fermats kleinem Satz:
Als Beispiel nehmen wir , und :
Multipliziert man beide Seiten der Gleichung mit , erhält man:
Dies bedeutet, dass man (mod pq) erhält, wenn man mit potenziert. Der RSA Algorithmus nutzt diese Tatsache für eine Ver- und Entschlüsselung. Wenn wir zwei Zahlen und finden mit:
, dann gilt
Ist die zu verschlüsselnde Nachricht, definieren wir als Verschlüsselungsschlüssel und als Entschlüsselungsschlüssel. Die beiden Schlüssel zum Ver- und Entschlüsseln sind also unterschiedlich (dies nennt man auch asymmetrische Verschlüsselung).
Schritt 1 – Verschlüsselung
Wir potenzieren die Nachricht mit , um die verschlüsselte Nachricht zu erhalten:
Die Sicherheit der RSA-Verfahrens beruht darauf, dass die Kenntnis von , und nicht ausreicht, um zu berechnen. Es gibt keinen eindeutigen Weg, um die -te Wurzel in modularer Arithmetik zu berechnen. Dies kann man zum Beispiel durch Fermats kleinen Satz selbst sehen:
Falls wir also die vierte Wurzel von 1 (mod 5) bestimmen wollen, können wir nicht wissen, ob die ursprüngliche Zahl 1, 2, 3 oder 4 ist.
Schritt 2 – Entschlüsselung
Um die verschlüsselte Nachricht wieder zu entschlüsseln, wird diese Nachricht mit (mod pq) potenziert. Mithilfe der Potenzgesetze erkennt man:
Der RSA-Algorithmus: Zusammenfassung des Prozesses
| Sender | Öffentlichkeit | Empfänger |
|---|---|---|
| Was sie wissen | ||
| x (zu verschlüsselnde Nachricht) e (Verschlüsselungsschlüssel) pq (zu verwendendes Modul) p oder q nicht einzeln bekannt | y (verschlüsselte Nachricht) e (Verschlüsselungsschlüssel) pq (zu verwendendes Modul) p oder q nicht einzeln bekannt | y (verschlüsselte Nachricht) d (Entschlüsselungsschlüssel) pq (zu verwendendes Modul) p und q sind einzeln bekannt, daher kann d errechnet werden |
| Was sie tun | ||
| Berechnen der verschlüsselten Nachricht y = xe (mod pq) | Das Wissen über e und pq reicht nicht aus, um y zu entschlüsseln | Berechnen der entschlüsselten Nachricht x = yd (mod pq) |
Beispiel
Wir verwenden p = 89, q = 151 und pq = 13439. Falls wir e = 7543 setzen, ist der zugehörige Entschlüsselungsschlüssel d = 7.
Wir verschlüsseln mit dem folgenden Python-Code die Beispielnachricht 8571:
def powerMod(a, b, c): # raises a to the power of b modulo c res = a % c for i in range(0, b - 1): res = a * res % c return res plainText = 8571 pq = 13439 e = 7543 encryptedMessage = powerMod(plainText, e, pq) print(encryptedMessage)
Als verschlüsselte Nachricht sollte sich 1134 ergeben.
Im zweiten Schritt entschlüsseln wir diese Nachricht wieder mit dem Entschlüsselungsschlüssel d=7:
def powerMod(a, b, c): # raises a to the power of b modulo c res = a % c for i in range(0, b - 1): res = a * res % c return res cipherText = 1134 pq = 13439 d = 7 decryptedMessage = powerMod(cipherText, d, pq) print(decryptedMessage)
Es ergibt sich wieder die ursprüngliche Nachricht 8571.
Das folgende Beispiel kann zur Überprüfung des RSA-Verfahrens verwendet werden:
und , d. h.
wie gefordert.
Ein passender Entschlüsselungsfaktor mit kann berechnet werden, wenn und teilerfremd sind (die Berechnung erfolgt durch den Erweiterten Euklidischen Algorithmus, worauf in dieser Stunde aus Zeitgründen verzichtet wurde).
Eine mögliche Schülerfrage ist, warum ein Angreifer nicht berechnen und damit den Code knacken kann. Die Antwort ist, dass man trotz Kenntnis von nicht von und einzeln kennt und damit auch nicht
.
Die Primfaktorzerlegung von ist sehr aufwändig, worin die Sicherheit des RSA-Verfahrens liegt.
SuS sollten zuerst das Beispiel implementieren und danach mit Aufgabe 3 weitermachen.
``
Aufgabe 3
Hinweis
Die Umwandlung von Zeichen in Zahlen kann mit ord() automatisiert werden. Diese Funktion liefert den ASCII-Code eines Zeichens. Die Umkehrfunktion chr() wandelt eine Zahl zurück in ein Zeichen. Je nach Python-Erfahrung können SuS automatische Umsetzung verwenden oder die Zeichen manuell in Zahlen umwandeln.
Die Beispielprogramme verwenden außerdem Reihungen und Schleifen zur Automatisierung der Verschlüsselung kompletter Nachrichten. Dies ist optional und abhängig vom Kenntnisstand der Lernenden.
Aufgabe 3a
Verwende pq=13439 und e = 5077, um die Nachricht QUANTUM zu verschlüsseln. Wandle jeden Buchstaben in eine Zahl um (A = 1, B = 2, …) und verschlüssele jeden Buchstaben einzeln.
Antworten: [4114, 5656, 1, 7072, 249, 5656, 12523]
Beispielcode:
def powerMod(a, b, c): # raises a to the power of b modulo c res = a % c for i in range(0, b - 1): res = a * res % c return res plainText = [ "Q", "U", "A", "N", "T", "U", "M" ] plainTextNumeric = [] for i in plainText: plainTextNumeric.append(ord(i) - 64) pq = 13439 e = 5077 cipherTextNumeric = [] for i in plainTextNumeric: cipherTextNumeric.append(powerMod(i, e, pq)) print(cipherTextNumeric)
Aufgabe 3b
Entschlüssele die Nachricht wieder mit pq=13439 und d = 13.
Beispielcode:
def powerMod(a, b, c): # raises a to the power of b modulo c res = a % c for i in range(0, b - 1): res = a * res % c return res cipherTextNumeric = [ 4114, 5656, 1, 7072, 249, 5656, 12523 ] pq = 13439 d = 13 plainTextNumeric = [] for i in cipherTextNumeric: plainTextNumeric.append(powerMod(i, d, pq)) print(plainTextNumeric) plainText = [] for i in plainTextNumeric: plainText.append(chr(i + 64)) print(plainText)
Der RSA-Algorithmus: Zusammenfassung
Das RSA-Verfahren besitzt zwei Schlüssel: einen öffentlichen Schlüssel und einen privaten (geheimen) Schlüssel. Der öffentliche Schlüssel besteht aus und ; der private Schlüssel besteht aus und den einzelnen Zahlen und .
Der öffentliche Schlüssel kann ohne Einschränkungen herausgegeben werden, da dieser nur zum Verschlüsseln, aber nicht zum Entschlüsseln verwendet werden kann. Möchte eine Bank zum Beispiel eine geheim zuhaltende Transaktion mit einem Kunden (z. B. eine PIN oder eine Kartenprüfnummer einer Kreditkarte) durchführen, kann die Bank den öffentlichen Schlüssel veröffentlichen. Der Kunde kann seine Information über die Operation
verschlüsseln und an die Bank übermitteln. Selbst wenn ein Hacker die Nachricht abfängt und ebenfalls im Besitz des öffentlichen Schlüssels der Bank ist, kann dieser nicht die ursprüngliche Nachricht berechnen. Nur die Bank selber kann mit dem privaten Schlüssel die Nachricht entschlüsseln, um die geheime Information zu lesen.
Das Brechen von RSA auf einem klassischen Computer
Um RSA zu brechen, muss man kennen. Dazu benötigt man jedoch
.
Obwohl öffentlich ist, ist das Produkt
nicht einfach zu bestimmen. Dazu müsste man in seine Primfaktoren zerlegen. Das ist für große Primzahlen und extrem aufwendig. Die folgende Aufgabe macht diese Herausforderung deutlich:
Aufgabe 4
Schreibe ein Programm, um die Zahl 900 239 055 271 631 zu faktorisieren. Stoppe die Zeit, die dein Programm benötigt, um die Faktoren und zu finden.
Je nach Python-Erfahrung benötigen Schülerinnen und Schüler eventuell Hinweise, beispielsweise:
- Schleife über alle Zahlen bis zur Quadratwurzel der Zielzahl erstellen.
- Zielzahl durch Schleifenvariable teilen. Falls sich eine ganze Zahl ergibt, ist ein Faktor gefunden.
- Um zu überprüfen, ob eine Zahl eine ganze Zahl ist, kann die Division x/i mit der Ganzzahldivision x//i verglichen werden (alternativ kann der Modulo-Befehl verwendet werden.)
- Bei positivem Ausgang des Tests Schleife beenden und Ergebnis ausgeben.
Antwort: 900,239,055,271,631 = 30,003,209 x 30,004,759
Beispielcode:
import time import math timeNow = time.time() x = 900239055271631 i = 2 foundFactors = False while foundFactors == False and i < math.sqrt(x): i += 1 if x / i == x // i: foundFactors = True if foundFactors == True: print(i) print(int(x / i)) print( "Time taken = " + str( int(time.time() - timeNow) ) + " seconds" )
Das Beispielprogramm nutzt die Systemzeitmessung zur Laufzeitanalyse (das ist optional). Die Aufgabe zeigt, dass selbst für vergleichsweise kleine Zahlen die Faktorisierung lange dauert. Auf dem verwendeten Rechner waren dies 25 Sekunden!
Zusammenfassung
Faktorisieren ist ein Beispiel für ein nur schwierig lösbares Problem: Es gibt eine Methode zum Finden der beiden Faktoren (Trial and Error Division), aber die benötigte Zeit wächst exponentiell mit der Größe der Zahl. Wählt man die beiden Primzahlen groß genug, könnten selbst Supercomputer das Produkt dieser Zahlen nicht in realistischer Zeit faktorisieren. In unserem Beispiel waren die beiden Primzahlen 8-stellig. In heutigen Implementationen von RSA werden typischerweise Primzahlen mit etwa je 300 Stellen verwendet. Damit ist derzeit RSA der Goldstandard der Internetsicherheit für abhörsichere Kommunikation.
Doch Quantencomputing könnte dieses Prinzip bedrohen: Mit Shors Algorithmus ließe sich die Komplexität der Faktorisierung reduzieren – so weit, dass RSA in Echtzeit gebrochen werden könnte. In der nächsten Lektion wirst du lernen, wie der Shor-Algorithmus die Sicherheit von RSA bedrohen kann und warum dies eine Revolution in der Kryptographie auslösen könnte.
Diese Seite teilen