RSA-Verfahren

Werbung
Das RSA-Verfahren
Klaus Becker
2010
2
Lehrplan - Leistungsfach
Ziel ist es, das RSAVerfahren als eines der
klassischen
asymmetrischen
Verschlüsselungsverfahren genauer zu
untersuchen, um die
Funktionsweise dieses
Verfahrens zu verstehen.
Die Vorgehensweise folgt
einem Vorschlag von
Witten und Schulz, der in
den folgenden Artikeln
beschrieben wird:
H. Witten, R.-H. Schulz:
RSA & Co. in der Schule,
Teil1.
LOG IN 140 S. 45 ff.
H. Witten, R.-H. Schulz:
RSA & Co. in der Schule,
Teil2.
LOG IN 143 S. 50 ff.
Lehrplan für das Leistungsfach
3
Teil 1
Experimente mit CrypTool
4
Experimente mit CrypTool
Einen ersten Eindruck vom RSA-Verfahren kann man sich mit dem
Software-Werkzeug CrypTool verschaffen. Dieses Werkzeug macht die
wichtigsten Schritte des RSA-Verfahrens transparent.
Experimente mit CrypTool lassen direkt erkennen, dass das RSA-Verfahren
auf Berechnungen mit Zahlen beruht.
Die Experimente führen aber noch nicht dazu, dass man versteht, warum
gerade dieses Verfahren heutzutage benutzt wird. Hierzu sind vertiefende
Untersuchungen erforderlich.
5
Experimente mit CrypTool
Mit den Menüpunkten
[Einzelverfahren][RSAKryptosystem][RSA-Demo...] kommst
du in Bereich, in dem das RSAVerfahren durchgespielt werden
kann.
Gib zunächst zwei verschiedene
Primzahlen in die dafür
vorgesehenen Felder ein. Mit
[Parameter aktualisieren] werden
dann die beiden Schlüssel erzeugt.
6
Experimente mit CrypTool
Wähle jetzt [Optionen für Alphabet
und Zahlensystem...] und lege die
vom Programm vorgesehenen
Optionen fest. Am besten
übernimmst du zunächst die
Einstellungen der folgenden
Abbildung (beachte das Leerzeichen
im Alphabet).
7
Experimente mit CrypTool
Jetzt kannst du Texte (mit Zeichen
aus dem voreingestellten Alphabet)
verschlüsseln und die
Verschlüsselung auch wieder
entschlüsseln.
8
Aufgabe
Probiere die oben beschriebenen Schritte selbst einmal aus. Variiere auch die möglichen
Vorgaben (z.B. eingegebene Primzahlen) und Einstellungsmöglichkeiten (z.B.
Alphabetoptionen).
9
Teil 2
Modulares Rechnen
10
Vorbemerkung
Das RSA-Verfahren basiert auf
modularem Rechnen. Um die Details
des RSA-Verfahren zu verstehen,
muss man modulares Rechnen
beherrschen und einige
zahlentheoretische Zusammenhänge
kennen.
Im Unterricht kann man die
mathematischen Grundlagen vorab
erarbeiten, oder beim Erarbeiten des
RSA-Verfahres - je nach Bedarf immer wieder zu den
mathematischen Betrachtungen
zurückkommen.
11
Uhrenaddition
Modulare Addition kennt man aus dem täglichen Leben.
Aufgabe:
Ergänze die in der Tabelle fehlenden Angaben zur Uhrzeit (Moskauer Zeit).
Wie rechnet man mit Uhrzeiten? Wie kann man z.B. direkt aus 17 und 149 zum Ergebnis 22
gelangen?
12
Modulare Gleichheit
Verallgemeinerte Uhrzeiten
Bei Beginn der Reise in Moskau ist es 17 Uhr. Nach 149 Stunden wird das Ziel Wladiwostok
erreicht. Es ist jetzt (17+149) Uhr bzw. 166 Uhr. Das entspricht - auch im fernen Sibirien - 22
Uhr. Man kann diese Uhrzeit leicht rechnerisch ermitteln indem man den Rest bei der Division
durch 24 ermittelt:
166 % 24 = 22
Uhrzeiten werden eigentlich nur mit den Zahlen 0, 1, ..., 23 angegeben. Im Alltag lässt man
auch manchmal die Zahl 24 zu. 24 Uhr ist dasselbe wie 0 Uhr. Die 24 ist - bei Uhrzeitangaben also gleich zu behandeln wie die 0.
31 Uhr und 55 Uhr (als verallgemeinerte Uhrzeiten) würden für dieselben Uhrzeiten stehen,
weil der zyklisch sich drehende und immer wieder bei 0 neu beginnende Uhrzeiger dieselbe
Stelle anzeigen würde. Rechnerisch zeigt sich das, indem beide Zahlen 31 und 55 denselben
Rest bei der Division durch 24 hinterlassen.
Def.: Vorgegeben sei eine natürliche Zahl n. Zwei natürliche Zahlen a und b heißen gleich
modulo n bzw. kongruent modulo n genau dann, wenn sie beide den gleichen Rest bei der
Division durch n erzeugen.
Beispiel: 31 und 55 sind gleich modulo 24, denn es gilt:
[31]%24 = 7 = [55]%24
Modulare Gleichheit
13
Zählen modulo vorgegebener Primzahlen
n:
0
1
2
3
4
5
6
7
8
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
[n]%3:
0
1
2
0
1
2
0
1
2
0
1
2
0
1
2
0
1
2
0
1
2
0
1
2
0
1
2
0
[n]%5:
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
[n]%15: 0
1
2
3
4
5
6
7
8
9 10 11 12 13 14
0
1
2
3
4
5
6
7
8
9 10 11 12
Satz (über modulare Gleichheit bzgl. Primzahlen):
p und q seien zwei vorgegebene verschiedene Primzahlen. Wenn zwei natürliche Zahlen a und
b sowohl gleich modulo p als auch gleich modulo q sind, dann sind sie auch gleich modulo
p*q.
Anders formuliert:
Aus [a]%p = [b]%p und [a]%q = [b]%q folgt [a]%(p*q) = [b]%(p*q).
14
Modulare Addition
Aufgabe:
(a) Führe die Rechnung für weitere Städte durch.
(b) Darf man für EKATERINBURG auch so rechen:
[17 + 26]%24 = [17]%24 + [26]%24 = ...
(c) Geht das auch für NOVOSIBIRSK? Was müsste man hier noch tun?
[17 + 46]%24 = [17]%24 + [46]%24 = ...
15
Modulare Addition
Vorgegeben sei eine natürliche Zahl n. Zwei natürliche
Zahlen a und b werden modulo n addiert, indem man sie
addiert und anschließend von der Summe den Rest bei der
Division durch n berechnet. Das Ergebnis ist also [a+b]%n.
Beachte, dass das Ergebnis bei der Addition modulo n
immer eine Zahl kleiner als n ist.
Aufgabe:
Erstelle selbst eine Verknüpfungstafel für die Addition
modulo n = 5.
Rechengesetz (Modulare Gleichheit bei der Addition):
Aus [a1]%n = [b1]%n und [a2]%n = [b2]%n folgt [a1+a2]%n = [b1+b2]%n.
Das erste Rechengesetz besagt, dass Zahlen, die modulo n gleich sind, auch zu gleichen
Additionsergebnissen modulo n führen.
Rechengesetz (Addition und iterierte Modulberechnung):
[a+b]%n = [[a]%n + [b]%n]%n
Das zweite Rechengesetz erlaubt es, bei der Addition modulo n zuerst die Summanden zu
verkleinern und dann erst die Addition durchzuführen.
16
Modulare Multiplikation
Vorgegeben sei eine natürliche Zahl n. Zwei natürliche
Zahlen a und b werden modulo n multipliziert, indem man
sie multipliziert und anschließend vom Produkt den Rest bei
der Division durch n berechnet. Das Ergebnis ist also
[a*b]%n. Beachte, dass das Ergebnis bei der Multiplikation
modulo n immer eine Zahl kleiner als n ist.
Aufgabe:
Erstelle selbst eine Verknüpfungstafel für die Multiplikation
modulo n = 8.
Rechengesetz (Modulare Gleichheit bei der Addition):
Aus [a1]%n = [b1]%n und [a2]%n = [b2]%n folgt [a1+a2]%n = [b1+b2]%n.
Das erste Rechengesetz besagt, dass Zahlen, die modulo n gleich sind, auch zu gleichen
Additionsergebnissen modulo n führen.
Rechengesetz (Addition und iterierte Modulberechnung):
[a+b]%n = [[a]%n + [b]%n]%n
Das zweite Rechengesetz erlaubt es, bei der Addition modulo n zuerst die Summanden zu
verkleinern und dann erst die Addition durchzuführen.
Aufgaben
17
Bestätige die Rechengesetze für modulare Addition und Multiplikation anhand von Beispielen.
Du kannst Python als Taschenrechner benutzen.
>>>
>>>
>>>
>>>
>>>
>>>
2
>>>
2
>>>
5
>>>
5
>>>
...
n = 14
a1 = 16
b1 = 19
a2 = 44
b2 = 75
a1%n
a2%n
b1%n
b2%n
(a1+b1)%n
18
Modulares Inverses
Def.: Zwei natürliche Zahlen a und b heißen modular invers
zueinander bezüglich n genau dann, wenn gilt:
[a*b]%n = 1.
Beispiel: [2*3]%5 = 1.
Die beiden Zahlen 2 und 3 sind also modular invers
zueinander bzgl. 5. Die Zahl 2 ist das modulare Inverse von
3 bzgl. des Moduls 5. Ebenso ist 3 das modulare Inverse
von 2 bzgl. des Moduls 5.
Aufgabe:
(a) Betrachte den Fall n = 5 (siehe Verknüpfungstafel oben). Bestimme zu a = 1, 2, 3, 4
jeweils das modulare Inverse bzgl. n.
(b) Betrachte den Fall n = 8 (siehe Folie 16). Für welche der Zahlen a = 1, 2, ..., 7 kann man
das modulare Inverse bzgl. n bestimmen?
(c) Betrachte den Fall n = 15. Hast du bereits eine Vermutung, für welche der Zahlen a = 1, 2,
..., 14 man das modulare Inverse bzgl. n bestimmen kann?
19
Existenz des modularen Inversen
Satz (über die Existenz des modularen Inversen):
Gegeben sei eine natürliche Zahl n. Das modulare Inverse zu einer Zahl a ungleich Null
existiert genau dann, wenn a und n keinen gemeinsamen Teiler größer als 1 haben - d.h.,
wenn ggT(a, n) = 1 gilt.
20
Berechnung des modularen Inversen
Entwickle eine Python-Funktion modInv(a, n), die das modulare Inverse von a bzgl. n
zurückgibt, sofern dieses Inverse existiert. Wenn keine solche Zahl existiert, soll der Wert -1
zurückgegeben werden.
Tipp: Man kann der Reihe nach alle in Frage kommenden Zahlen durchprobieren.
21
Modulare Potenz
Vorgegeben sei eine natürliche Zahl n. Eine natürliche Zahl a wird mit einer natürlichen Zahl x
modulo n potenziert, indem man sie mit x potenziert und anschließend von der Potenz den
Rest bei der Division durch n berechnet. Das Ergebnis ist also [ax]%n. Beachte, dass das
Ergebnis bei der Potenzbildung modulo n immer eine Zahl kleiner als n ist.
Aufgabe:
(a) Berechne [34]%5.
(b) Berechne [64]%5. Berechne auch [([([([6]%5)*6]%5)*6]%5)*6]%5. Was stellst du fest?
(c) Welche Vorteile ergeben sich bei großen Zahlen, wenn man [ax]%n wie folgt berechnet:
[(...([([a]%n)*a]%n)...)*a]%n ?
Aufgabe:
Berechne [a(p-1)]%p für verschiedene natürliche Zahlen a und verschiedene Primzahlen p. Für
welche Zahlen erhält man als Ergebnis 1? Du kannst hierzu ein einfaches Python-Programm
schreiben, das die Berechnungen für verschiedene Primzahlen p und natürliche Zahlen a
übernimmt.
22
Kleiner Fermatscher Satz
Satz (Kleiner Fermatscher Satz):
Sei p eine Primzahl und a eine natürliche Zahl, die kein Vielfaches von p ist. Dann gilt
[a(p-1)]%p = 1
Gegeben sei eine Primzahl p und eine natürliche Zahl, die kein Vielfaches von p ist (z.B. p=5
und a = 12).
Wenn man [1*a]%p, [2*a]%p, [3*a]%p, ..., [(p-1)*a]%p berechnet, so erhält man als
Ergebnisse die Zahlen 1, 2, 3, ..., p-1 - allerdings in anderer Reihenfolge.
Hieraus lässt sich mit einigen Rechengesetzen folgender Zusammenhang herleiten:
[(1*a)*(2*a)*(3*a)*...*((p-1)*a)]%p = [1*2*3*...*(p-1)]%p
Umgeformt erhält man:
[1*2*3*...*(p-1)*a(p-1)]%p = [1*2*3*...*(p-1)]%p
Hieraus kann man mit einigen zusätzlichen Überlegungen schließen:
[a(p-1)]%p = 1
23
Teil 3
Verschlüsseln mit modularer Addition
24
Vorbemerkung
Als Vorstufe zum RSA-Verfahren betrachten wir hier ein Verfahren, das auf
modularer Addition beruht und bereits viele Ähnlichkeiten zum RSAVerfahren aufweist.
Der Vorteil dieser Vorgehensweise besteht darin, dass wir an das sehr
einfache Caesar-Verfahren anknüpfen können und durch Verallgemeinerung
dieses Verfahrens zu den zahlenbasierten Verfahren gelangen.
Den Anfang macht Caesar
25
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
Quelltext:
SALVEASTERIX
Schlüssel: D
Geheimtext:
VDOYHDVWHULA
26
Caesar-Verfahren mit Zahlen
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
Entschlüsselung:
Verarbeitung von Zahlen
Decodierung:
Umwandlung von
Zahlen in Zeichen
A → 00
B → 01
...
Z → 25
A,S,T,E,R,I,X
00,18,19,04,17,08,23
(00 + 3) % 26 = 03
(18 + 3) % 26 = 21
...
(23 + 3) % 26 = 00
00,18,19,04,17,08,23
(03 + 23) % 26 = 00
(21 + 23) % 26 = 18
...
(00 + 23) % 26 = 23
03,21,22,07,20,11,00
A → 00
B → 01
...
Z → 25
00,18,19,04,17,08,23
03,21,22,07,20,11,00
00,18,19,04,17,08,23
D,V,W,H,U,L,A
27
Caesar-Variation: zusätzliche Zeichen
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
Entschlüsselung:
Verarbeitung von Zahlen
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
N,A,C,H, ,R,O,M
14,01,03,08,00,18,15,13
(14 + 9) % 27 = 23
(01 + 9) % 27 = 10
...
(13 + 9) % 27 = 22
14,01,03,08,00,18,15,13
(23 + 18) % 27 = 14
(10 + 18) % 27 = 01
...
(22 + 18) % 27 = 13
23,10,12,17,09,00,24,22
' ' → 00
'A' → 01
...
'Z' → 26
23,10,12,17,09,00,24,22
14,01,03,08,00,18,15,13
14,01,03,08,00,18,15,13
N,A,C,H, ,R,O,M
28
Caesar-Variation: verallgeinerte Addition
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
(e, m) = (9, 30)
Entschlüsselung:
Verarbeitung von Zahlen
(d, m) = (21, 30)
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
D,A, ,I,S,T, ,E,S
04,01,00,09,19,20,00,05,19
(04 + 18) % 30 = 22
(01 + 18) % 30 = 19
...
(19 + 18) % 30 = 07
04,01,00,09,19,20,00,05,19
(22 + 12) % 30 = 04
(19 + 12) % 30 = 01
...
(07 + 12) % 30 = 19
22,19,18,27,07,08,18,23,07
' ' → 00
'A' → 01
...
'Z' → 26
22,19,18,27,07,08,18,23,07
04,01,00,09,19,20,00,05,19
04,01,00,09,19,20,00,05,19
D,A, ,I,S,T, ,E,S
29
Caesar-Variation: größere Blocklänge
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
(e, m) = (112233, 321321)
Entschlüsselung:
Verarbeitung von Zahlen
(d, m) = (209088, 321321)
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
(80112 + 112233) %
321321 = 192345
(80112 + 112233) %
321321 = 233733
HAL,LO
80112, 121500
80112, 121500
192345, 233733
(192345 + 209088) %
321321 = 192345
(233733 + 209088) %
321321 = 121500
192345, 233733
' ' → 00
'A' → 01
...
'Z' → 26
80112, 121500
80112, 121500
HAL,LO
30
Verfahren mit modularer Addition
Schritt 1: Wahl der Blocklänge und Zerlegung des Textes
Die Blocklänge legt die Länge der Texteinheiten fest, die mit Zahlen codiert werden und anschließend verschlüsselt werden.
Je größer die Blocklänge, desto mehr Zahlen benötigt man zur Codierung der Texteinheiten.
Bei einer Blocklänge 3 wird beispielweise der Text 'CAESAR' wie folgt in Texteinheiten zerlegt:
'CAE','SAR'
Bei einer Zerlegung eines Textes kann es vorkommen, dass eine Texteinheit übrig bleibt, die nicht mehr die gesamte
Blocklänge hat. In diesem Fall füllen wir den Text mit zusätzlichen Zeichen (hier Leerzeichen) auf:
'HAL','LO '
31
Verfahren mit modularer Addition
Schritt 2: Wahl der Codierung
Codierung von Zeichenblöcken:
Codierung von Zeichenblöcken:
Die Codierung ordnet jeder Texteinheit eine
natürliche Zahl zu. Die Zuordnung muss eindeutig
sein, so dass eine Decodierung möglich ist.
'
'
' -> 000
' -> 0000
' A' -> 001
' A' -> 0001
' B' -> 002
' B' -> 0002
...
...
Codierung des Alphabets:
' Z' -> 026
' Z' -> 0026
' ' -> 00
'A ' -> 027
'A ' -> 0100
'A' -> 01
'AA' -> 028
'AA' -> 0101
...
...
...
'Z' -> 26
'ZZ' -> 728
'ZZ' -> 2626
32
Verfahren mit modularer Addition
Schritt 3: Wahl des Moduls und der Verschiebezahl
Die Modulzahl n ist eine beliebige natürliche Zahl. Sie muss nur so gewählt werden, dass sie größer als die größtmögliche
Codezahl einer Texteinheit ist. Die zu wählende Größe hängt demnach von der Blocklänge und der gewählten Codierung ab.
Die Verschiebezahl e zum Verschlüsseln (e-ncrypt) ist eine beliebige natürliche Zahl, die kleiner als die Modulzahl n ist.
Beide zusammen - Verschiebezahl und Modul - werden zur Verschlüsselung benötigt. Das Zahlenpaar (e, n) bildet den
Schlüssel zur Verschlüsselung eines Textes. Dieser Schlüssel wird auch öffentlicher Schlüssel genannt.
Schritt 4: Bestimmung des Gegenschlüssels
Die Verschiebezahl d zum Entschlüsseln (d-ecrypt) ergibt sich direkt aus e und n: Es muss e+d=m gelten. Also ist d = n - e.
Das Zahlenpaar (d, n) bildet den Schlüssel zur Entschlüsselung eines Textes. Dieser Schlüssel wird auch privater Schlüssel
genannt.
33
Verfahren mit modularer Addition
Schritt 5: Verschlüsselung codierter Texte
Zur Verschlüsselung eine Codezahl x benötigt man den öffentlichen Schlüssel (e, m). Die Verschlüsselung erfolgt hier durch
modulare Addition:
x -> [x + e]%n
Schritt 6: Entschlüsselung codierter Texte
Zur Entschlüsselung eine Codezahl y benötigt man den privaten Schlüssel (d, n). Die Entschlüsselung erfolgt analog zur
Verschlüsselung:
y -> [y + d]%n
34
Aufgabe
Benutze unsere Standardcodierung mit Blocklänge 2. Wähle einen öffentlichen Schlüssel (wie
z.B. (567, 2911)) und verschlüssele eine selbst gewählte Nachricht mit dem oben
beschriebenen Verfahren mit modularer Addition (Version 3).
Gib die Nachricht an deinen Nachbarn weiter. Teile ihm auch den benutzten öffentlichen
Schlüssel mit. Dein Nachbar soll jetzt die Nachricht wieder entschlüsseln.
35
Korrektheit
Korrektheit:
Die Entschlüsselung macht die Verschlüsselung rückgängig:
x → [x + e]%n → [[x + e]%n + d]%n = [x + [e + d]%n]%n = [x]%n = x
36
Prinzip von Kerckhoff
Die Sicherheit eines Kryptosystems darf nicht von der Geheimhaltung des
Algorithmus abhängen. Die Sicherheit darf sich nur auf die Geheimhaltung
des Schlüssels gründen.
Vgl. A. Beutelspacher: Kryptologie. Vieweg 1996
Das Prinzip wurde erstmals formuliert im Buch "La cryptographie militaire" von Jean Guillaume
Hubert Victor Francois Alexandre Auguste Kerckhoffs van Nieuwenhof (1835 bis 1903).
37
Sicherheit
Sicherheit:
Das additive Chiffrierverfahren ist nicht sicher, da man aus dem öffentlichen Schlüssel sofort
den privaten Schlüssel bestimmen kann.
38
Implementierung
Aufgabe 1: Eine Implementierung testen
Lade die Datei chiffriersystemModularesAddierenAufgabe1.txt (siehe inf-schule) herunter.
Diese Datei enthält eine ganze Reihe von Funktionen, die Teilaufgaben beim Verfahren mit
modularer Addition übernehmen. Teste alle Funktionen und beschreibe das Verhalten der
Funktionen in Worten. Passende Funktionsaufrufe sind als Docstrings bereits angegeben.
Aufgabe 2: Eine Implementierung nutzen
Mit Funktionen der Implementierung aus Aufgabe 1 kannst jetzt interaktiv das Verfahren mit
modularer Addition durchspielen. Führe selbst weitere Tests durch. Du kannst die
Funktionsaufrufe auch in einem kleinen Testprogramm zusammenfassen.
>>> block = 2
>>> oeffentlicherSchluessel = (2102, 3000)
>>> privaterSchluessel = (898, 3000)
>>> quelltext = 'ASTERIX'
>>> quellcode = codieren(quelltext, block)
>>> quellcode
[119, 2005, 1809, 2400]
>>> geheimcode = verschluesseln(quellcode, oeffentlicherSchluessel)
>>> geheimcode
[2221, 1107, 911, 1502]
>>> entschluesseltercode = verschluesseln(geheimcode, privaterSchluessel)
>>> entschluesseltercode
[119, 2005, 1809, 2400]
>>> entschluesseltertext = decodieren(entschluesseltercode, block)
>>> entschluesseltertext
'ASTERIX'
Implementierung
39
Aufgabe 3: Eine Implementierung abändern
Die Implementierung aus Aufgabe 1 benutzt zur Codierung das Aneinanderfügen von Codes
bei Zeichenblöcken:
'
' -> 0000
' A' -> 0001
' B' -> 0002
...
' Z' -> 0026
'A ' -> 0100
'AA' -> 0101
...
'ZZ' -> 2626
Ändere die Implementierung so ab, dass der ASCII-Code zur Umwandlung von Zeichen in
Zahlen benutzt wird. Bei Zeichenblöcken sollen die Codezahlen durch systematisches
Durchzählen ermittelt werden. In CrypTool wird hierzu die Einstellung "b-adisch" gewählt.
Experimentiere erst mit CrypTool und passe dann die Implementierung der Codierung
geeignet an.
40
Implementierung
Aufgabe 4: Eine Implementierung selbst entwickeln
Lade die Datei chiffriersystemModularesAddierenAufgabe4.txt (siehe inf-schule) herunter.
Diese Datei enthält bereits eine ganze Reihe von Funktionsbeschreibungen, die Teilaufgaben
beim Verfahren mit modularer Addition übernehmen können. Ergänze die Implementierung
dieser Funktionen und teste die Funktionen wie in Aufgabe 2.
41
Teil 3
Verschlüsseln mit modularer Multiplikation
42
Vorbemerkung
Statt modularer Addition verwenden wir hier modulare Multiplikation als
Grundlage eines Verschlüsselungsverfahres. Dieses Verfahen kann ebenfalls
als Vorstufe zum RSA-Verfahren angesehen werden. Genau wie beim RSAVerfahren hängt die Sicherheit des hier betrachteten Verfahrens davon ab,
ob man über schnelle Algorithmen für bestimmte Problemstellungen
verfügt.
Multiplikation statt Addition
43
A(lice)
B(ob)
öffentl. Schlüssel
von B(ob)
pivat. Schlüssel
von B(ob)
(e, n)
(d, n)
"HALLO ..."
"HALLO ..."
Codierung
f(x, (e,n))
x0, x1, x2, ...
=
Klartext
[x+e]%n
f*(y, (d,n))
y0, y1, y2, ...
Geheimtext
Codierung
=
x0, x1, x2, ...
[y+d]%n
Klartext
Entschlüsselungsfunktion
Verschlüsselungsfunktion
A(lice)
B(ob)
öffentl. Schlüssel
von B(ob)
pivat. Schlüssel
von B(ob)
(e, n)
(d, n)
"HALLO ..."
"HALLO ..."
Codierung
f(x, (e,n))
x0, x1, x2, ...
=
Klartext
[x*e]%n
Verschlüsselungsfunktion
f*(y, (d,n))
y0, y1, y2, ...
Geheimtext
Codierung
=
[y*d]%n
Entschlüsselungsfunktion
x0, x1, x2, ...
Klartext
44
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
Codierung:
Umwandlung von
Zeichen in Zahlen
Statt Addition ...
' ' → 00
'A' → 01
...
'Z' → 26
N,I,X, ,L,O,S
14,09,24,00,12,15,19
(14 + 7) % 30 = 21
(09 + 7) % 30 = 16
...
(19 + 7) % 30 = 26
14,09,24,00,12,15,19
(21 + 23) % 30 = 14
(16 + 23) % 30 = 09
...
(26 + 23) % 30 = 19
21,16,01,07,19,22,26
' ' → 00
'A' → 01
...
'Z' → 26
21,16,01,07,19,22,26
14,09,24,00,12,15,19
14,09,24,00,12,15,19
N,I,X, ,L,O,S
45
... benutze Multiplikation!
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
Verschlüsselung:
Verarbeitung von Zahlen
Codierung:
Umwandlung von
Zeichen in Zahlen
' ' → 00
'A' → 01
...
'Z' → 26
(14 * 7) % 30 = 08
(09 * 7) % 30 = 03
...
(19 * 7) % 30 = 13
N,I,X, ,L,O,S
14,09,24,00,12,15,19
14,09,24,00,12,15,19
08,03,18,00,24,15,13
(08 * x) % 30 = 14
(03 * x) % 30 = 09
...
(13 * x) % 30 = 19
08,03,18,00,24,15,13
' ' → 00
'A' → 01
...
'Z' → 26
14,09,24,00,12,15,19
14,09,24,00,12,15,19
N,I,X, ,L,O,S
Verfahren und seine Korrektheit
46
Schlüsselerzeugung:
A(lice)
öffentl. Schlüssel
von B(ob)
(e, n)
B(ob)
Wähle n größer als die
maximale Codezahl.
pivat. Schlüssel
von B(ob)
Wähle e mit e <n.
(d, n)
Bestimme d mit
[e*d]%n = 1.
"HALLO ..."
"HALLO ..."
Codierung
f(x, (e,n))
x0, x1, x2, ...
=
Klartext
[x*e]%n
f*(y, (d,n))
=
y0, y1, y2, ...
x0, x1, x2, ...
[y*d]%n
Geheimtext
Verschlüsselungsfunktion
Codierung
Entschlüsselungsfunktion
Korrektheit:
Die Entschlüsselung macht die Verschlüsselung rückgängig:
x → [x * e]%n → [[x * e]%n * d]%n = [x * [e * d]%n]%n = [x * 1]%n = x
Es muss hierzu folgende Schlüsselbedingung erfüllt sein:
[e * d]%n = 1
d.h.: d ist modulares Inverses zu e bzgl. n.
Klartext
47
Implementierung
Aufgabe 1: Eine Implementierung testen
Lade die Datei chiffriersystemModularesMultiplizieren.txt (siehe inf-schule) herunter. Diese
Datei enthält eine ganze Reihe von Funktionen zur Implementierung des Verfahrens mit
modularer Multiplikation.
Teste die Implementierung, indem du das Verfahren mit modularer Multiplikation interaktiv
durchspielst.
Aufgabe 2: Implementierungen analysieren
Vergleiche die Implementierungen zum Verfahren mit modularer Addition und zum Verfahren
mit modularer Multiplikation. Die Implementierungen unterscheiden sich nur an einer Stelle.
Welche Stelle ist das?
Sicherheit
48
Schlüsselerzeugung:
A(lice)
öffentl. Schlüssel
von B(ob)
(e, n)
Wähle n größer als die
maximale Codezahl.
Wähle e mit e <n.
Bestimme d mit
[e*d]%n = 1.
B(ob)
pivat. Schlüssel
von B(ob)
(?, n)
???
???
Codierung
???
Klartext
f(x, (e,n))
=
[x*e]%n
f*(y, (d,n))
y0, y1, y2, ...
Geheimtext
Codierung
=
[y*d]%n
???
Klartext
Entschlüsselungsfunktion
Verschlüsselungsfunktion
Mr(s) X
Sicherheit:
Die Sicherheit des multiplikativen Chiffrierverfahrens hängt davon ab, ob man zur Zahl e aus
dem öffentlichen Schlüssel das modulare Inverse d bzgl. n bestimmen kann.
49
Codierung:
Umwandlung von
Zeichen in Zahlen
Aufgabe
' ' → 00
'A' → 01
...
'Z' → 26
.,.,.,.,.,.,.
Verschlüsselung:
öffentlicher Schlüssel
(e, m) = (16, 33)
Entschlüsselung:
24, 12, 15, 29, 23, 12, 13
privater Schlüssel
(d, m) = (..., ...)
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
50
Codierung:
Umwandlung von
Zeichen in Zahlen
Aufgabe
' ' → 00
'A' → 01
...
'Z' → 26
..,..,..,..,..,..,..
Verschlüsselung:
öffentlicher Schlüssel
(e, m) = (381, 800)
Entschlüsselung:
030,235,314,316,305,253,368
privater Schlüssel
(d, m) = (..., ...)
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
51
Bestimmung des modularen Inversen
Ein naiver Ansatz besteht darin, der Reihe nach alle Zahlen durchzuprobieren, bis man das
gewünschte Ergebnis gefunden hat.
Beispiel: e = 16; n = 33
[16*1]%33 = 16; [16*2]%33 = 32; ...; [16*31]%33 = 1
Diesen naiven Ansatz kann man auch leicht implementieren:
def modInv(e, n):
gefunden = False
d = 1
while d <= n and not gefunden:
if (e * d) % n == 1:
gefunden = True
else:
d = d + 1
if d > n:
d = -1
return d
52
Bestimmung des modularen Inversen
Aufgabe:
Teste den Baustein modInv mit selbst mit selbst gewählten Beispielen. Überprüfe auch die
Richtigkeit der Ergebnisse.
Aufgabe:
(a) Teste den Baustein mit großen Zahlen. Bestimme hierzu das modulare Inverse von
a = 775517959261225265313877628572204089387832653836742449
bzgl. des Moduls
n = 1000010000100001000010000100001000010000100001000010000.
(b) Bestimme zunächst mit dem Resultat aus (a) das modulare Inverse von b = 49 bzgl. des
Moduls
n = 1000010000100001000010000100001000010000100001000010000.
Bestimme anschließend das gesuchte modulare Inverse mit dem vorgegebenen Baustein.
Welches Problem tritt hier auf? Hast du eine Vermutung, warum das Problem auftritt.
53
Bestimmung des modularen Inversen
def modInvMitAusgaben(e, n):
gefunden = False
d = 1
while d <= n and not gefunden:
if d % 10000000 == 0:
print("Anzahl der Versuche: ", d)
if (e * d) % n == 1:
gefunden = True
else:
d = d + 1
if d > n:
d = -1
return d
>>> modInvMitAusgaben(49,
1000010000100001000010000100001000010000100001000010000)
Anzahl der Versuche: 10000000
Anzahl der Versuche: 20000000
Anzahl der Versuche: 30000000
für 10 Millionen
...
Überprüfungen benötigt
man mehr als 1 Sekunde!
54
Bestimmung des modularen Inversen
Beispiel:
d = 49
n = 1000010000100001000010000100001000010000100001000010000
modInv(d, n)
Um 10 000 000 (= 107) Zahlen durchzuprobieren, benötigt ein Rechner derzeit mehr als 1s.
Da das erwartete Ergebnis 775517959261225265313877628572204089387832653836742449
eine 54-stellige Zahl ist, wird der Rechner eine Zeit benötigen, die in der Größenordnung von
1047s liegt. Dies sind mehr als 1039 Jahre. Bedenkt man, dass das Universum ein Alter von
etwa 1010 Jahre hat, dann zeigt sich, wie ungeeignet das naive Vorgehen ist.
Für größere Zahlen ist der naive Algorithmus unbrauchbar. Für die
gezeigten Zahlen benötigt ein Rechner länger, als das Universum alt ist.
55
Vielfachsummensatz
Ein besseres Verfahren zur Bestimmung des modularen Inversen basiert auf folgendem
Zusammenhang ("Vielfachsummensatz", "Lemma von Bézout", "Lemma von Bachet"):
Für je zwei natürliche Zahlen a und b gibt es ganze Zahlen x und y mit ggT(a,b)=x*a+y*b.
Beispiele:
a = 3; b = 4: ggT(3, 4) = 1 = (-1)*3 + 1*4
a = 6; b = 9: ggT(6, 9) = 3 = (-1)*6 + 1 * 9
a = 41; b = 192: ggT(41, 192) = 1 = 89*41 + (-19)*192
56
Erweiterter euklidischer Algorithmus
Gegeben: a = 884; b = 320
Gesucht: ggT(a, b) = x*a + y*b
(1) 884 = 2*320 + 244
→ 244 = 884 - 2*320 = (1*884 + 0*320) - 2*(1*320 + 0*884) = 1*884 - 2*320
(2) 320 = 1*244 + 76
→ 76 = 320 - 1*244 = (0*884 + 1*320) - 1*(1*884 - 2*320)) = 3*320 - 1*884
(3) 244 = 3*76 + 16
→ 16 = 244 - 3*76 = (1*884 - 2*320) - 3*(3*320 - 1*884) = 4*884 - 11*320
(4) 76 = 4*16 + 12
→ 12 = 76 - 4*16 = (3*320 - 1*884) - 4*(4*884 - 11*320) = 47*320 - 17*884
(5) 16 = 1*12 + 4
→ 4 = 16 - 1*12 = (4*884 - 11*320) - 1*(47*320 - 17*884) = 21*884 - 58*320
(6) 12 = 3*4 + 0
Ergebnis:
ggT(884, 320) = 4 = 21*884 + (- 58)*320
57
Aufgabe
Bestimme analog die Darstellung für a = 30 und b = 7.
Gegeben: a = 30; b = 7
Gesucht: ggT(a, b) = x*a + y*b
58
Aufgabe
Das Struktogramm zeigt, wie der erweiterte euklidische Algorithmus
mit Variablen und Kontrollstrukturen beschrieben werden kann. Im
Folgenden ist ein Ablaufprotokoll für die Eingaben a = 884 und b =
320 skizziert. Mache dir anhand dieses Ablaufprotokolls die
Arbeitsweise des Algorithmus klar. Die unten gezeigten
Berechnungsschritte sollten sich im Ablaufprotokoll widerspiegeln.
Bachet
Eingabe: a, b
aalt := a
amitte := b
xalt := 1
xmitte := 0
y alt := 0
y mitte := 1
SOLANGE amitte <> 0
(1) 884 = 2*320 + 244
→ 244 = 884 - 2*320 = (1*884 + 0*320) - 2*(1*320 + 0*884) = 1*884 - 2*320
(2) 320 = 1*244 + 76
→ 76 = 320 - 1*244 = (0*884 + 1*320) - 1*(1*884 - 2*320)) = 3*320 - 1*884
(3) 244 = 3*76 + 16
→ 16 = 244 - 3*76 = (1*884 - 2*320) - 3*(3*320 - 1*884) = 4*884 - 11*320
(4) 76 = 4*16 + 12
→ 12 = 76 - 4*16 = (3*320 - 1*884) - 4*(4*884 - 11*320) = 47*320 - 17*884
(5) 16 = 1*12 + 4
→ 4 = 16 - 1*12 = (4*884 - 11*320) - 1*(47*320 - 17*884) = 21*884 - 58*320
(6) 12 = 3*4 + 0
q := aalt div amitte
aneu := aalt mod amitte
xneu := xalt - q*xmitte
y neu := y alt - q*y mitte
xalt := xmitte
xmitte := xneu
y alt := y mitte
y mitte := y neu
aalt := amitte
amitte := aneu
Ausgabe: aalt, xalt, y alt
59
Erweiterter euklidischer Algorithmus
Geg.: a = 884; b = 320; Ges.: ggT(a, b) = x*a + y*b
aalt:884 = a:884
amitte:320 = b:320
xalt:1 = 1
xmitte:0 = 0
yalt:0 = 0
ymitte:1 = 1
{aalt:884 = xalt:1 * a: 884 + yalt:0 * b:320;
amitte:320 = xmitte:0 * a:884 + ymitte:1 * b:320}
(1) 884 = 2*320 + 244
→ 244 = 884 - 2*320 =
(1*884 + 0*320) - 2*(1*320 + 0*884) =
1*884 - 2*320
q: 2 = aalt: 884 / amitte: 320
aneu:244 = aalt:884 % amitte:320
xneu:1 = xalt:1 - xmitte:0 * q:2
yneu:-2 = yalt:0 - ymitte:1 * q:2
xalt:0 = xmitte:0
xmitte:1 = xneu:1
yalt:1 = ymitte:1
ymitte:-2 = yneu:-2
aalt:320 = amitte:320
amitte:244 = aneu:244
{aalt:320 = xalt:0 * a:884 + yalt:1 * b:320;
amitte:244 = xmitte:1 * a:884 + ymitte:-2 * b:320}
Bachet
Eingabe: a, b
aalt := a
amitte := b
xalt := 1
xmitte := 0
y alt := 0
y mitte := 1
SOLANGE amitte <> 0
q := aalt div amitte
aneu := aalt mod amitte
xneu := xalt - q*xmitte
y neu := y alt - q*y mitte
xalt := xmitte
xmitte := xneu
y alt := y mitte
y mitte := y neu
aalt := amitte
amitte := aneu
Ausgabe: aalt, xalt, y alt
60
Erweiterter euklidischer Algorithmus
{aalt:320 = xalt:0 * a:884 + yalt:1 * b:320;
amitte:244 = xmitte:1 * a:884 + ymitte:-2 * b:320}
(2) 320 = 1*244 + 76
→ 76 = 320 - 1*244 =
(0*884 + 1*320) - 1*(1*884 - 2*320)) =
3*320 - 1*884
q: 1 = aalt: 320 / amitte: 244
aneu:76 = aalt:320 % amitte:244
xneu:-1 = xalt:0 - xmitte:1 * q:1
yneu:3 = yalt:1 - ymitte:-2 * q:1
xalt:1 = xmitte:1
xmitte:-1 = xneu:-1
yalt:-2 = ymitte:-2
ymitte:3 = yneu:3
aalt:244 = amitte:244
amitte:76 = aneu:76
{aalt:244 = xalt:1 * a:884 + yalt:-2 * b:320;
amitte:76 = xmitte:-1 * a:884 + ymitte:3 * b:320}
Bachet
Eingabe: a, b
aalt := a
amitte := b
xalt := 1
xmitte := 0
y alt := 0
y mitte := 1
SOLANGE amitte <> 0
q := aalt div amitte
aneu := aalt mod amitte
xneu := xalt - q*xmitte
y neu := y alt - q*y mitte
xalt := xmitte
xmitte := xneu
y alt := y mitte
y mitte := y neu
aalt := amitte
amitte := aneu
Ausgabe: aalt, xalt, y alt
61
Aufgabe
Teste die Implementierung des erweiterten Euklidischen Algorithmus..
def erweiterterEuklidischerAlgorithmus(a, b):
aalt = a
amitte = b
xalt = 1
xmitte = 0
yalt = 0
ymitte = 1
while amitte != 0:
q = aalt // amitte
aneu = aalt - q * amitte
xneu = xalt - xmitte * q
yneu = yalt - ymitte * q
xalt = xmitte
xmitte = xneu
yalt = ymitte
ymitte = yneu
aalt = amitte
amitte = aneu
print(amitte, '=', xmitte, '*', a, '+', ymitte, '*', b)
return (aalt, xalt, yalt)
62
Bestimmung des modularen Inversen
Mit Hilfe der Ausgaben des erweiterten euklidischen Algorithmus lässt sich das modulare
Inverse bestimmen:
Beispiel 1: Gesucht wird das modulare Inverse von a = 41 bzgl. m = 192.
Python liefert:
>>> erweiterterEuklidischerAlgorithmus(41, 192)
(1, 89, -19)
Umformungen:
1 = 89*41 + (-19)*192
1 - (-19)*192 = 89*41
[1 - (-19)*192]% 192 = [89*41]%192
[1 + 19*192]% 192 = [89*41]%192
1 = [89*41]%192
Ergebnis: b = 89
63
Bestimmung des modularen Inversen
Mit Hilfe der Ausgaben des erweiterten euklidischen Algorithmus lässt sich das modulare
Inverse bestimmen:
Beispiel 2: Gesucht wird das modulare Inverse von a = 17 bzgl. m = 192 .
Python liefert:
>>> erweiterterEuklidischerAlgorithmus(17, 192)
(1, -79, 7)
Umformungen:
1 = (-79)*17 + 7*192
1 - 7*192 = (-79)*17
1 - 7*192 + 192*17 = (-79+192)*17
1 + 10*192 = 113*17
[1 + 10*192]%192 = [113*17]%192
1 = [113*17]%192
Ergebnis: b = 113
64
Aufgabe
Beispiel 3: Gesucht wird das modulare Inverse von a = 7 bzgl. m = 30.
Python liefert:
>>> erweiterterEuklidischerAlgorithmus(
Umformungen:
Ergebnis: b =
,
)
65
Bestimmung des modularen Inversen
Mit Hilfe der Ausgaben des erweiterten euklidischen Algorithmus lässt sich das modulare
Inverse bestimmen:
def modInv(a, m):
(ggt, x, y) = erweiterterEuklidischerAlgorithmus(a, m)
if ggt > 1:
return -1
else:
if x < 0:
x = x + m
return x
Teste die Implementierung insbesondere für große Zahlen:
d = 49
m = 1000010000100001000010000100001000010000100001000010000
modInv(d, m)
Welche Konsequenzen ergeben sich hieraus für die Sicherheit des Chiffrierverfahrens mit
modularer Multiplikation?
66
Sicherheit
Sicherheit:
Das "multiplikative" Chiffrierverfahren ist nicht sicher, da man aus dem öffentlichen Schlüssel
mit Hilfe des erweiterten euklidischen Algorithmus den privaten Schlüssel recht schnell
bestimmen kann.
Die "Unsicherheit" basiert hier also darauf, dass man ein schnelles Verfahren gefunden hat, um
das modulare Inverse einer Zahl zu bestimmen.
67
Teil 4
Verschlüsseln mit modularem Potenzieren
68
Verschlüsseln d. modulares Rechnen
modulares
Addieren
Verschlüsselung mit öffentl.
Schlüssel (e, n)
x → [x + e]%n
Entschlüsselung mit privat.
Schlüssel (d, n)
y → [y + d]%n
modulares
Multiplizieren
Verschlüsselung mit öffentl.
Schlüssel (e, n)
x → [x * e]%n
Entschlüsselung mit privat.
Schlüssel (d, n)
y → [y * d]%n
modulares
Potenzieren
Verschlüsselung mit öffentl.
Schlüssel (e, n)
x → [x ** e]%n
Entschlüsselung mit privat.
Schlüssel (d, n)
y → [z ** d]%n
69
Verschlüsseln d. modulares Potenzieren
Codierung:
Umwandlung von
Zeichen in Zahlen
Verschlüsselung:
öffentlicher Schlüssel
(e, m) = (13, 77)
Entschlüsselung:
privater Schlüssel
(d, m) = (37, 77)
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
A,S,T,E,R,I,X
01,19,20,05,18,09,24
(01 ** 13) % 77 = 01
(19 ** 13) % 77 = 61
...
(24 ** 13) % 77 = 52
01,19,20,05,18,09,24
(01 ** 37) % 77 = 01
(61 ** 37) % 77 = 19
...
(52 ** 37) % 77 = 24
01,61,...,...,...,...,52
' ' → 00
'A' → 01
...
'Z' → 26
01,61,...,...,...,...,52
01,19,20,05,18,09,24
01,19,20,05,18,09,24
A,S,T,E,R,I,X
70
Implementierung
Beim Rechnen mit Potenzen erhält man große Zahlen:
>>> 52 ** 37
3105444088679819357273546406651335246066988648897330641813635072
>>> 3105444088679819357273546406651335246066988648897330641813635072 % 77
24
Zum schnellen modularen Potenzieren sollte man daher die folgende Funktion modpot
benutzen.
def modpot(x, y, m):
pot = 1
while y > 0:
if y % 2 == 1:
pot = (pot * x) % m
y = y - 1
else:
x = (x * x) % m
y = y // 2
return pot
71
Implementierung
Aufgabe:
(a) Ergänze geeignete Ausgabeanweisungen und versuche, auf diese Weise die Idee des
Algorithmus zum schnellen modularen Potenzieren herauszufinden.
(b) Der Algorithmus zur modularen Potenzbildung ist eine geringfügige Erweiterung des
Algorithmus zum schnellen Potenzieren. Was wird hier ergänzt? Warum führt das zu einem
korrekten Ergebnis? Zur Klärung der Korrektheit musst du einen mathematischen
Zusammenhang heranziehen.
def modpot(x, y, m):
pot = 1
while y > 0:
if y % 2 == 1:
pot = (pot * x) % m
y = y - 1
else:
x = (x * x) % m
y = y // 2
return pot
72
Implementierung
Aufgabe:
Besorge dir eine Implementierung des Verfahrens mit modularer Multiplikation und ändere
diese Implementierung in geeigneter Weise ab.
Du kannst dir alternativ hierzu auch die Datei chiffriersystemModularePotenz.txt (siehe infschule) herunter laden. Diese Datei enthält eine ganze Reihe von Funktionen zur
Implementierung des RSA-Verfahrens.
Mit den Funktionen der Implementierung kannst jetzt interaktiv das RSA-Verfahren
durchspielen. Probiere das mit selbst gewählten Daten aus.
Zur Kontrolle: Vergleiche die erzielten Ergebnisse mit denen, die CrypTool (mit passenden
Einstellungen) liefert.
73
Erzeugung der Schlüssel
74
Erzeugung der Schlüssel
Vorbereitung:
Beispiel:
Wähle zwei verschiedene Primzahlen p und q.
p = 7; q = 11
öffentlicher Schlüssel:
Berechne n = p*q.
n = 77
Berechne φ(n) = (p-1)*(q-1).
φ(n) = 60
Wähle eine Zahl e mit 1 < e < φ(n) , die
teilerfremd zu φ(n) ist.
z. B. e = 13
Der öffentliche Schlüssel ist (e, n).
(13, 77)
("Vernichte p, q, φ(n).")
privater Schlüssel:
Berechne d so, dass [e*d]%φ(n) = 1 ist.
d = 37
Der private Schlüssel ist (d, n).
(37, 77)
Korrektheit des RSA-Verfahren
75
RSA-Schlüsselerzeugung:
Wähle zwei verschiedene
Primzahlen p und q.
A(lice)
öffentl. Schlüssel
von B(ob)
(e, n)
Berechne n = pq und
(n) = (p-1)(q-1).
Wähle e mit 1 < e < (n)
und ggT(e,(n)) = 1.
B(ob)
pivat. Schlüssel
von B(ob)
(d, n)
Bestimme d mit
[cd]%(n) = 1.
"HALLO ..."
Codierung
"HALLO ..."
f(x, (e,n))
x0, x1, x2, ...
=
Klartext
[xe]%n
RSA-Verschlüsselungsfunktion
f*(y, (d,n))
=
y0, y1, y2, ...
[yd]%n
Geheimtext
RSA- Entschlüsselungsfunktion
Korrektheit:
Die Entschlüsselung macht die Verschlüsselung rückgängig:
x → [xe]%n → [([xe]%n)d]%n = [x(e * d)]%n = [x]%n = x
Es muss hierzu folgende Schlüsseleigenschaft erfüllt sein:
[x(e * d)]%n = x für alle x < n
Codierung
x0, x1, x2, ...
Klartext
76
Korrektheit des RSA-Verfahren
Beh.: [x(e * d)]%n = x für alle x < n
Begr.:
Schritt 1: Es gilt n = p*q mit zwei verschiedenen Primzahlen p und q. Wir zeigen:
[x(e*d)]%p = [x]%p und [x(e*d)]%q = [x]%q für alle Zahlen x < n
Es reicht, den Nachweise für eine der beiden Primzahlen p und q zu führen. Der Nachweis für
die andere Primzahl verläuft dann völlig analog. Wir betrachten im Folgenden die Primzahl p.
Fall 1: p und x sind nicht teilerfremd.
Da p eine Primzahl ist, muss in diesem Fall p ein Teiler von x sein. Die Primzahl p muss dann
auch ein Teiler der Potenz x(e*d) sein. Es folgt:
[x]%p = 0 und [x(e*d)]% p = 0
Also: [x(e*d)]%p = [x]%p
Fall 2: p und x sind teilerfremd.
Nach dem kleinen Fermatschen Satz git dann: [x(p-1)]%p = 1
Nach der Konstruktion der Schlüssel gilt: [e*d]%φ(n) = 1
Da φ(n) = (p-1)*(q-1), gibt es also eine Zahl a mit e*d = a*(p-1)*(q-1)+1.
...
77
Korrektheit des RSA-Verfahren
... Jetzt können wir folgende Umformungen vornehmen:
[x(e*d)]%p =
[x(a*(p-1)*(q-1)+1)]%p =
[x(a*(p-1)*(q-1))*x]%p =
[x(a*(p-1)*(q-1))]%p * [x]%p =
[([x(p-1)]%p)(a*(q-1))]%p * [x]%p =
[1(a*(q-1))]%p * [x]%p =
1 * [x]%p =
[x]%p
Damit ist die Behauptung von Schritt 1 gezeigt.
Schritt 2:
Aus [x(e*d)]%p = [x]%p und [x(e*d)]%q = [x]%q
(für alle Zahlen x < n) können wir jetzt (mit dem Satz über modulare Gleichheit bzgl.
Primzahlen) schließen:
[x(e*d)]%(p*q) = [x]%(p*q)
Wegen n = p*q und x < n gilt dann: [x(e*d)]%n = x
Sicherheit
78
RSA-Schlüsselerzeugung:
Wähle zwei verschiedene
Primzahlen p und q.
A(lice)
öffentl. Schlüssel
von B(ob)
(e, n)
Berechne n = pq und
(n) = (p-1)(q-1).
Wähle e mit 1 < e < (n)
und ggT(e,(n)) = 1.
B(ob)
pivat. Schlüssel
von B(ob)
(?, n)
Bestimme d mit
[cd]%(n) = 1.
???
Codierung
???
Klartext
f(x, (e,n))
=
[xe]%n
???
f*(y, (d,n))
y0, y1, y2, ...
Geheimtext
Codierung
=
[yd]%n
???
Klartext
RSA- Entschlüsselungsfunktion
RSA-Verschlüsselungsfunktion
Mr(s) X
Sicherheit:
Die Sicherheit des RSA-Verfahrens hängt davon ab, ob man aus dem öffentlichen Schlüssel (e,
n) den privaten Schlüssel (d, n) (effizient) bestimmen kann.
79
Codierung:
Umwandlung von
Zeichen in Zahlen
Aufgabe
' ' → 00
'A' → 01
...
'Z' → 26
Verschlüsselung:
öffentlicher Schlüssel
(e, m) = (19, 65)
48, 9, 60, 38, 60, 0, 58, 47, 31,
60, 59, 59, 60, 0, 1, 31, 59, 0,
58, 1, 38, 38, 9, 60, 14
Entschlüsselung:
privater Schlüssel
(d, m) = (..., ...)
Decodierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
Aufgabe
80
öffentlicher Schlüssel
(e, m) = (1432765433173537777777, 1914269284601333234385791628203)
privater Schlüssel
(d, m) = (..., ...)
Codierung:
Umwandlung von
Zahlen in Zeichen
' ' → 00
'A' → 01
...
'Z' → 26
0703995545688427802027825362902, 0076119838972138298619729763565
Angriff auf das RSA-Verfahren
81
RSA-Schlüsselerzeugung:
Wähle zwei verschiedene
Primzahlen p und q.
A(lice)
öffentl. Schlüssel
von B(ob)
(e, n)
Berechne n = pq und
(n) = (p-1)(q-1).
Wähle e mit 1 < e < (n)
und ggT(e,(n)) = 1.
B(ob)
pivat. Schlüssel
von B(ob)
(?, n)
Bestimme d mit
[cd]%(n) = 1.
???
Codierung
???
Klartext
???
f(x, (e,n))
=
[xe]%n
f*(y, (d,n))
=
y0, y1, y2, ...
[yd]%n
Geheimtext
RSA-Verschlüsselungsfunktion
Codierung
???
Klartext
RSA- Entschlüsselungsfunktion
Beispiel (Aufgabe): n = 65 -> p = 5 und q = 13
Aus den beiden Primzahlen p und q kann Mr(s). X die Zahl φ(n) = (p-1) * (q-1) berechnen.
Beispiel (Aufgabe 1): p = 5 und q = 13 -> φ(n) = 48
Mr(s). X weiß zudem, dass die Zahl d modulares Inverses von e bzgl. φ(n) ist. Mit dem
erweiterten euklidischen Algorithmus kann Mr(s). X diese Zahl d bestimmen.
Beispiel (Aufgabe 1): e = 19 und φ(n) = 48: [19*d]%48 = 1 -> d = 43
Mr(s). X kennt jetzt den privaten Schlüssel und kann den Geheimtext entschlüsseln.
Sicherheit RSA-Verfahren
82
RSA-Schlüsselerzeugung:
Wähle zwei verschiedene
Primzahlen p und q.
A(lice)
öffentl. Schlüssel
von B(ob)
(e, n)
Berechne n = pq und
(n) = (p-1)(q-1).
Wähle e mit 1 < e < (n)
und ggT(e,(n)) = 1.
B(ob)
pivat. Schlüssel
von B(ob)
(?, n)
Bestimme d mit
[cd]%(n) = 1.
???
Codierung
???
Klartext
f(x, (e,n))
=
[xe]%n
RSA-Verschlüsselungsfunktion
???
f*(y, (d,n))
y0, y1, y2, ...
Geheimtext
Codierung
=
[yd]%n
???
Klartext
RSA- Entschlüsselungsfunktion
Sicherheit:
Die Sicherheit des RSA-Verfahrens hängt davon ab, ob man die Zahl n in vertretbarer Zeit in
ihre Primfaktoren p und q zerlegen kann.
Bis heute gibt es keine schnellen Algorithmen, um eine Zahl in ihre Primfaktoren zu zerlegen.
Das RSA-Verfahren ist bei groß gewählten Primzahlen recht sicher, da man aus dem
öffentlichen Schlüssel den privaten Schlüssel bisher nicht in angemessener Zeit bestimmen
kann.
83
Teil 5
Primzahlalgorithmen
84
Primzahltest
Zur Durchführung des RSA-Verfahrens benötigt man große Primzahlen. Man
wählt heute Primzahlen, die mit mindestens 512 Bit dargestellt werden.
Das sind Zahlen in der Größenordnung 2512, also Zahlen mit mehr als 150
Dezimalstellen.
Zur Bestimmung großer Primzahlen geht man wie folgt vor. Man erzeugt
eine Zufallszahl im gewünschten Größenbereich und testet, ob es sich um
eine Primzahl handelt. Hierzu benötigt man geeignete Primzahltests. Da es
sehr viele Primzahlen im gewünschten Bereich gibt, muss man nicht in der
Regel allzu viele Zahlen testen.
85
Primzahlen
Primzahlen sind natürliche Zahlen, die nur durch 1 und sich selbst ohne Rest teilbar sind.
Beispiele: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, ...
86
Primzahltest
Aufgabe:
Aus der Primzahleigenschaft ergibt sich direkt ein einfacher Algorithmus, mit dem man bei
einer natürlichen Zahl n überprüfen kann, ob es sich um eine Primzahl handelt.
(a) Formuliere den Algorithmus in Struktogrammform.
(b) Implementiere und teste den Algorithmus.
(c) Entwickle Möglichkeiten zur Verbesserungen des einfachen Algorithmus.
87
Ein einfaches Testverfahren
def primzahl(n):
if n <= 2:
if n < 2:
prim = False
else:
prim = True
else:
if n % 2 == 0:
faktorgefunden = True
else:
faktorgefunden = False
t = 3
while t*t <= n and not faktorgefunden:
if n % t == 0:
faktorgefunden = True
else:
t = t + 2
prim = not faktorgefunden
return prim
88
Ein einfaches Testverfahren
primzahlen = [
11,
101,
1009,
10007,
100003,
1000003,
10000019,
100000007,
1000000007,
10000000019,
100000000003,
1000000000039,
10000000000037,
100000000000031,
1000000000000037,
10000000000000061,
100000000000000003,
1000000000000000003,
10000000000000000051,
100000000000000000039,
1000000000000000000117,
...]
def primzahl(n):
...
from time import *
for p in primzahlen:
t1 = clock()
ergebnis = primzahl(p)
t2 = clock()
t = t2 - t1
print("Primzahl: ", p,
"Rechenzeit: ", t)
89
>>>
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
...
Laufzeitverhalten
11 Rechenzeit: 5.86666741164e-06
101 Rechenzeit: 8.3809534452e-06
1009 Rechenzeit: 1.50857162014e-05
10007 Rechenzeit: 3.54793695847e-05
100003 Rechenzeit: 0.000101968266917
1000003 Rechenzeit: 0.000324342898329
10000019 Rechenzeit: 0.00104817791088
100000007 Rechenzeit: 0.00332500359683
1000000007 Rechenzeit: 0.0105655886432
10000000019 Rechenzeit: 0.0407208178693
100000000003 Rechenzeit: 0.140259725747
1000000000039 Rechenzeit: 0.447675891768
10000000000037 Rechenzeit: 1.41919042783
100000000000031 Rechenzeit: 4.55093566361
1000000000000037 Rechenzeit: 14.3208156344
10000000000000061 Rechenzeit: 45.2250185429
100000000000000003 Rechenzeit: 144.197546336
Aufgabe:
Schätze ab, wie lange eine Überprüfung einer 150-stelligen Primzahl in etwa dauert.
90
Probabilistische Testverfahren
In der Praxis benutzt man heute oft sogenannte probabilistische Testverfahren, da sie sehr
effizient arbeiten. Probabilistischen Testverfahren funktionieren nach dem folgenden Prinzip:
Bei Übergabe einer natürlichen Zahl n erhält man als Rückgabe entweder "n ist keine Primzahl"
oder "n ist wahrscheinlich eine Primzahl". Diese Testverfahren liefern also keine absolute
Gewissheit, wenn sie das Ergebnis "n ist wahrscheinlich eine Primzahl" produzieren. Die
übergebene Zahl n kann mit einer bestimmten Wahrscheinlichkeit auch keine Primzahl sein.
Allerdings ist diese Wahrscheinlichkeit sehr gering, so dass man die Unsicherheit oft in Kauf
nimmt.
Eines dieser probabilistischer Testverfahren ist das Miller-Rabin-Verfahren, das im Folgenden
getestet werden soll. Beachte, dass die Wiederholungszahl 20 (s.u.) die
Fehlerwahrscheinlichkeit beeinflusst. Setzt man diese Wiederholungszahl auf einen größeren
Wert, so nimmt die Fehlerwahrscheinlichkeit ab.
91
Miller-Rabin-Test
import random
def miller_rabin_pass(a, n):
d = n - 1
s = 0
while d % 2 == 0:
d = d >> 1
s = s + 1
a_to_power = pow(a, d, n)
if a_to_power == 1:
return True
for i in range(s-1):
if a_to_power == n - 1:
return True
a_to_power = (a_to_power * a_to_power) % n
return a_to_power == n - 1
def miller_rabin_test(n):
for repeat in range(20):
a = 0
while a == 0:
a = random.randrange(n)
if not miller_rabin_pass(a, n):
return False
return True
92
>>>
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Primzahl:
Laufzeitverhalten
11 Rechenzeit: 0.000118730173807
101 Rechenzeit: 0.000144990494602
1009 Rechenzeit: 0.000217904789575
10007 Rechenzeit: 0.000181866689761
100003 Rechenzeit: 0.000280761940414
1000003 Rechenzeit: 0.00031400638908
10000019 Rechenzeit: 0.000371276237622
100000007 Rechenzeit: 0.000415974655997
1000000007 Rechenzeit: 0.000454527041845
10000000019 Rechenzeit: 0.000569346104044
100000000003 Rechenzeit: 0.000617117538682
1000000000039 Rechenzeit: 0.000658184210563
10000000000037 Rechenzeit: 0.000720482631172
100000000000031 Rechenzeit: 0.000901511225589
1000000000000037 Rechenzeit: 0.000982527108892
10000000000000061 Rechenzeit: 0.00114316204993
100000000000000003 Rechenzeit: 0.00111746045936
1000000000000000003 Rechenzeit: 0.0011973588822
10000000000000000051 Rechenzeit: 0.00138956208121
100000000000000000039 Rechenzeit: 0.00151862876427
1000000000000000000117 Rechenzeit: 0.00166445735422
10000000000000000000009 Rechenzeit: 0.00163987322411
100000000000000000000117 Rechenzeit: 0.0019804192991
1000000000000000000000007 Rechenzeit: 0.0020670224847
10000000000000000000000013 Rechenzeit: 0.00199578438042
100000000000000000000000067 Rechenzeit: 0.00229358759284
1000000000000000000000000103 Rechenzeit: 0.00245701618502
10000000000000000000000000331 Rechenzeit: 0.00275649558813
100000000000000000000000000319 Rechenzeit: 0.003038374989
93
Primfaktorzerlegung
260
2
*
2
*
5
*
13
Eine der wichtigsten Eigenschaften von Primzahlen ist, dass sie als Bausteine der natürlichen
Zahlen angesehen werden können.
Satz: Jede natürliche Zahl lässt sich als Produkt von Primzahlen schreiben. Diese Darstellung
ist bis auf die Reihenfolge der Faktoren eindeutig.
Beispiel: 260 = 2*2*5*13 = 22*5*13
Man nennt die Primzahlen, die in einer Produktdarstellung einer gegebenen Zahl vorkommen,
auch Primfaktoren der Zahl.
Das Faktorisierungsproblem besteht darin, eine vorgegebene Zahl in ein Produkt aus
Primfaktoren zu zerlegen.
94
Aufgabe
(a) Bei kleineren Zahlen kann man eine Primfaktorzerlegung oft direkt angeben. Bestimme eine
Primfaktorzerlegung von n = 48 und n = 100.
(b) Bei größeren Zahlen sollte man systematisch vorgehen, um die Primfaktoren zu
bestimmen. Bestimme eine Primfaktorzerlegung von n = 221 und n = 585.
(c) Entwickle zunächst einen Algorithmus zur Primfaktorzerlegung. Beschreibe in einem ersten
Schritt in Worten das Verfahren, das du zur Primfaktorzerlegung von Zahlen benutzt.
Beschreibe das Verfahren anschließend mit einem Struktogramm. Entwickle dann ein
Programm zur Primfaktordarstellung. Hinweis: In Python bietet es sich an, eine Funktion
primfaktoren(n) zu erstellen, die die Liste der Primfaktoren zurückgibt.
95
Ein einfaches Faktorisierungsverfahren
ALGORITHMUS primfaktoren(n):
# Übergabe: n = 51
initialisiere die Liste faktoren: faktoren = []
# Initialisierung
initialisiere die Hilfsvariable z: z = n
faktoren = []
{faktoren -> []}
SOLANGE z > 1:
z=n
{z -> 51}
bestimme den kleinsten Primfaktor p von z mit
Probedivisionen
# Probedivisionen
füge p in die Liste faktoren ein
z % 3 -> 0
z = z // p
Rückgabe: faktoren
z % 2 -> 1
# Aktualisierung
p=z
{p -> 3}
faktoren = faktoren + [p] {faktoren -> [3]}
z = z // p
{z -> 17}
# Probedivisionen
Aufgabe:
Bestimme mit (einer geeigneten
Implementierung) der Funktion
primfaktoren(n) die Primfaktorzerlegung der
beiden Zahlen 484639526894037745950720
und 565765434324543216797351. Was
stellst du fest? Stelle eine Vermutung auf,
warum es hier zu einem unterschiedlichen
Laufzeitverhalten kommt.
z % 2 -> 1
z % 3 -> 2
z % 4 -> 1
z % 5 -> 2
# Aktualisierung
p=z
{p -> 17}
faktoren = faktoren + [p] {faktoren -> [3, 17]}
z = z // p
# Rückgabe: [3, 17]
{z -> 1}
Laufzeitmessungen
96
from faktorisierung import primfaktoren
testzahlen = [
from time import *
11,
testzahlen = [...]
101,
for z in testzahlen:
1009,
t1 = clock()
10007,
ergebnis = primfaktoren(z)
100003,
t2 = clock()
1000003,
t = t2 - t1
10000019,
print("Zahl:
", z)
100000007,
print("Primfaktoren:", ergebnis)
1000000007,
print("Rechenzeit: ", t)
10000000019,
print()
100000000003,
Hinweis:
Um Gesetzmäßigkeiten herauszufinden,
sollte man systematisch vorgehen.
Aufgabe:
Führe die Messungen durch. Kannst du
anhand der Zahlen erste Zusammenhänge
erkennen? Kannst du Prognosen erstellen,
wie lange man wohl bis zum nächsten
Ergebnis warten muss?
1000000000039,
10000000000037,
100000000000031,
1000000000000037,
10000000000000061,
100000000000000003,
1000000000000000003,
10000000000000000051,
100000000000000000039,
...]
97
Zusammenhänge und Prognosen
Gesetzmäßigkeit:
Wenn man die Anzahl der Stellen der
Ausgangszahl um 2 erhöht, dann erhöht sich
die Laufzeit um den Faktor 10. Jede
zusätzliche Stelle bei der Ausgangszahl führt
also dazu, dass die Laufzeit mit dem Faktor
√10 multipliziert wird.
...
Es handelt sich hier um ein exponentielles
Wachstumsverhalten, das man mathematisch
mit einer Exponentialfunktion beschreiben
kann: Wenn k die Anzahl der Stellen der
Ausgangszahl angibt, dann erhält man eine
Laufzeit vom Typ L(k) = c*(√10)k mit einer
Konstanten c.
Primfaktoren: [10000000000037]
Prognose:
Wenn die Zahl 100 Stellen haben soll, also
88 Stellen mehr als eine 12-stellige Zahl, so
benötigt man nach der gefundenen
Gesetzmäßigkeit 1044-mal so lange wie bei
der 12-stelligen Zahl - also etwa 1044
Sekunden.
Zahl:
Zahl:
1000000000039
Primfaktoren: [1000000000039]
Rechenzeit: 0.906267137304
Zahl:
10000000000037
Rechenzeit: 2.88270213114
Zahl:
100000000000031
Primfaktoren: [100000000000031]
Rechenzeit: 9.1279123464
1000000000000037
Primfaktoren: [1000000000000037]
Rechenzeit: 28.5701070946
Zahl:
10000000000000061
Primfaktoren: [10000000000000061]
Rechenzeit: 91.2736900919
...
98
Fazit
Algorithmen spielen bei der Entwicklung von Chiffriersystemen eine große
Rolle.
Im Fall des RSA-Verfahrens benötigt man einerseits gute Algorithmen, um
das Verfahren überhaupt effizient durchführen zu können (z. B. schnell ein
modulares Inverses bestimmen; schnell eine modulare Potenz bestimmen).
Andererseits ist das Verfahren so angelegt, dass bestimmte Operation mit
den bisher bekannten Algorithmen mit vertretbarem Rechenaufwand nicht
durchgeführt werden können.
99
Literaturhinweise
Folgende Materialien wurden hier benutzt:
H. Witten, R.-H. Schulz: RSA & Co. in der Schule, Teil1. LOG IN 140 S. 45 ff
H. Witten, R.-H. Schulz: RSA & Co. in der Schule, Teil2. LOG IN 143 S. 50 ff
K. Merkert: http://www.hsg-kl.de/faecher/inf/krypto/rsa/index.php
http://www.cryptool.org/download/RSA/RSA-Flash-de/player.html
Herunterladen