6. Sichere Schlüsselübergabe mit RSA

Werbung
HBS Kryptologie
6. Sichere Schlüsselübergabe mit RSA
Alice, Bob und Eve
Die im letzten Kapitel erstellte Transnigma genügt bei einer Schlüssellänge von 64
Zeichen auch den allerhöchsten Sicherheitsansprüchen. Wenn es dann in einigen
Jahren Rechner gibt, die zum Entschlüsseln nur noch einige Tage benötigen, dann
wird die Schlüssellänge einfach auf 128 erhöht. Danach ist das Verfahren für einige
Jahre wieder sicher.
Wenn keine wirklich prinzipiell anderen Computer wie die heutigen gebaut werden,
dann wird es eine Schlüssellänge N geben, die in absehbarer Zeit nicht verlängert
werden muss. Erst etwas völlig Neues, wie etwa ein Quanten-Computer, kann dann
dafür sorgen, dass man das Verfahren überdacht werden muss.
Warum dann ein Kapitel 7, wenn das erlernte Verfahren angeblich so perfekt ist?
Eine Bank, wie die Deutsche Bank, hat einige Hunderttausend Kunden. Mit jedem
dieser Kunden muss sie Informationen über öffentliche Kanäle austauschen.
„Homebanking“, um ein Beispiel zu nennen,
läuft über das Internet und nicht, wie das
nebenstehende Bild glauben machen will, durch
Abgabe und Inempfangnahme von Bargeld bei
der Bank.
Wie soll hier die Schlüsselübergabe
funktionieren? Muss jeder Kunde zur Bank
laufen, um dort seine persönlichen Schlüssel in
Empfang zu nehmen? Die Methode wäre sehr
sicher. Ist das durchführbar? Die Online-Bank
IngDiBa beispielsweise hat ihren Sitz in
Frankfurt. Ein Kunde, der in Hamburg wohnt
darf dann mehrmals im Jahr nach Frankfurt
reisen...
Wenn das zu anstrengen ist, dann könnte die Schlüssel ja auch über Telephon oder
Briefpost verteilen. Aber: Ist das sicher?
Damit ist das Ziel dieses Kapitels klar: Wir benötigen eine Methode, bei der eine
Schlüsselübergabe sicher und bequem abläuft.
Das Problem ist ein Paradox, über das die Menschen schon sehr lange beschäftigt:
Wenn zwei Menschen (Alice und Bob) sich ein Geheimnis mitteilen wollen, müssen
sie sich zuvor bereits ein Geheimnis (den Schlüssel) mitgeteilt haben. Ist dies denn
ohne persönlichen Kontakt möglich?
Zwei Jahrtausende galt das Paradox als unauflösbsar. Anfang letzten Jahrhunderts
wurden sogar einige „Beweise“ veröffentlicht, die auf vielen Seiten sorgfältig
darlegten, dass ein derartiger Austausch unmöglich ist.
Ist es wirklich unmöglich?
89
HBS Kryptologie
Ein Gedankenexperiment: Alice und Bob wollen ohne vorherigen persönlichen
Kontakt geheime Informationen austauschen. Das Problem ist die neugierige
Briefträgerin Eve. Sie öffnet mitunter die zuzustellenden Briefe und liest sie, um auf
dem Laufenden zu sein.
Daher besorgt sich Alice eine Kiste, die man
mit zwei Vorhängeschlössern verschließen
kann. Alice und Bob kaufen je ein Schloss.
Den Schlüssel dazu geben sie nicht aus der
Hand.
Alice steckt den geheimen Brief in die Kiste
und schließt mit ihrem Schloss (A) ab. Dann
schickt sie die Kiste an Bob. Die Briefträgerin
Eve ist verärgert, denkt sich aber, dass auch
Bob die Kiste nicht öffnen kann.
Das ist richtig. Daher befestigt nun Bob seinerseits sein Schloss (B) an der Kiste,
schließt ab und übergibt die Kiste wieder Eve, die sie mit grimmigem Blick an Alice
ausliefert. Die wiederum löst nun ihr eigenes Schloss und schickt sie an Bob zurück.
Und Bob braucht nun nur seinen Schlüssel nehmen, um an den Brief zu kommen....
Umständlich, - aber es funktioniert! Und schon glaubt man, nun endlich auch eine
Lösung, die ohne die Kisten mit den Schlössern auskommt, gefunden zu haben. Es
ist ja nicht schwer, die gedanklichen Schlösser durch Verschlüsselungen und die
Schlüssel mit Schlüsselwörtern zu identifizieren.
Aufgabe 1:
Verschlüssele folgenden Text mit der Transnigma: „Leider zu früh gefreut“.
Verwende dazu einen nur Dir bekannten Schlüssel. Das Ergebnis übergibst Du
Deinem Nachbarn. Der verschlüsselt den Text ebenfalls wieder mit einem nur ihm
bekannten Schlüssel und gibt ihn dann zurück.
Den Text lädst Du in die Transnigma und drückst auf „alles entschlüsseln“. Das
Ergebnis gibst Du wieder Deinem Nachbarn, der es dann mit seinem Schlüssel zu
entschlüsseln versucht.
Klappt das? Wenn nein: Warum nicht?
Hier ein möglicher Ablauf:
LEIDERZUFRUEHGEFREUT
Originaltext
IROXVNLCMNDHETETCOZC
von Alice mit dem Schlüssel ABCD verschlüsselt
ERNNTIVSKOPSKZFHGCZA
von Bob mit dem Schlüssel EFGH verschlüsselt
GENXLTEUXMIVJOYACADR
von Alice entschlüsselt
FLJVEPKGAMZEGELKKXZR
von Bob entschlüsselt.
Es klappt also nicht! Schade! Es wäre so einfach gewesen.....
90
HBS Kryptologie
Kein Grund, die Flinte ins Korn zu werfen. Woran liegt es denn, dass am Schluss
nicht der Originaltext rauskommt?
Wir erinnern uns: Wenn man einen Geheimtext beispielsweise mit zwei
verschiedenen Transpositionen verschlüsselt hatte, mussten, um an den Klartext zu
kommen, die beiden Entschlüsselung in genau umgekehrter Reihenfolge ablaufen.
War die erste Transposition mit Faktor 5 durchgeführt und die zweite mit Faktor 7,
dann musste zuerst die Transposition mit Faktor 7 rückgängig gemacht werden und
dann die mit Faktor 5. Und genau das geht, wie man leicht einsieht, in dem „KistenModell“ leider nicht.
Aufgabe 2:
Wie Aufgabe 1, aber als Verschlüsselung dient uns hier eine monoalphabitische
Methode, wie zum Beispiel CaesarKomfortSuper
Ist das die Rettung?
(Damit kein Missverständnis entsteht: Es gibt sehr wohl monoalphabetische
Verschlüsselungen, die eine Umkehrung der Entschlüsselungs-Reihenfolge nicht
zulassen!)
Die Kisten-Idee für eine sichere Schlüsselübergabe ist zwar mit den obigen
Verfahren nicht realsierbar, - muss sie deswegen überhaupt nicht umsetzbar sein?
Bei der Konstruktion der Software-Engima wurde eine Funktion (enigma) benutzt,
die aus den Buchstaben des Schlüssels eine unendliche Folge von weiteren Buchstaben errechnete. Dies geschah in derartig komplexen Rechnungen, dass ohne
Kenntnis der Funktion, eine Rekonstruktion des Gesamtschlüssels aus den
Buchstaben des Schlüssels nicht oder nur sehr schwer möglich ist.
Trotzdem liegt hier eine Schwäche des Systems: Woher kann man wissen, dass die
geheimgehaltene Funktion nicht eines Tages verraten wird?
Besser wäre also eine Funktion, von der man weiß, was sie tut, und die dennoch
keinen Rückschluss vom Ergebnis auf die eingegebene Zahl zuließe. Eine Art
„Einwegfunktion“.
Die meisten Funktionen, die wir aus der Schule kennen, taugen dafür nicht. Die
Funktion „mal zwei“ kann sehr leicht durch „geteilt durch zwei“ wieder rückgängig
gemacht werden.
Selbst bei der Quadratfunktion kann mit
Hilfe eines Taschenrechners durch
„Wurzel ziehen“ die ursprüngliche Zahl
wieder erhalten werden.
Die Amerikaner W.Diffie und M.Hellman
(Jahrgang 1944 und 1945) verwandten
ihre ganze Energie auf die Suche einer
derartigen Funktion.
Hellmann
Diffie
91
HBS Kryptologie
Die größte Hoffnung setzten sie auf die „modulo“-Funktionen. Wie wir weiter oben
schon gesehen haben, wird mit z.B. 36 mod 5 der Rest bei der Division von 36 durch
5 bezeichnet, also 36 mod 5 = 1 mod 5
Dass dies tatsächlich eine Einwegfunktion ist wird klar, wenn man feststellt, dass 41
mod 5 ebenfalls 1 ergibt. Man kann daher nicht vom Ergebnis 1 auf die ursprüngliche
Zahl 41 schließen. Leider kann das niemand, - auch der berechtigte Empfänger
nicht. Es muss daher einen Weg geben, der es diesem Empfänger erlaubt, durch
eine Zusatzinformation, die nur ihm nützt, die richtige Ursprungszahl zu finden.
Die Lösung ist eine Funktion, wie 17 x mod 11. Weshalb und wie das funktioniert
wollen wir im Folgenden darlegen.
P
P
Eine Einwegfunktion
Die folgende Tabelle zeigt, weshalb das Ergebnis von 17 x mod 11 nur sehr schwer
auf das zugrunde liegende x schließen lässt:
P
x
7x
7 x mod11
P
P
P
P
1
7
7
2
49
5
3
343
2
4
2401
3
P
5
16807
10
6
117649
4
Der besseren Verständlichkeit wegen sind die Zahlen sehr klein gewählt worden.
Ganz anders sähe es aus, wenn man etwa 731 x mod 321611 als Einwegfunktion
verwenden würde.
Von welchem x kommt das Ergebnis 205013 ? Dazu würde man auch mit Taschenrechner Stunden oder Tage brauchen. Wenn man weiß, dass das Ergebnis x = 2332
ist, dann kann man sich vorstellen, dass auch ein Computer einige Zeit braucht, bis
er bei 2332 in der Tabelle angekommen ist.
Aber auch die letzt genannten Zahlen sind noch immer nicht besonders groß. Was,
wenn erst bei x = 28726552299876254332155419987635421211 die gesuchte Zahl
liegt? Das ist auch von Super-Rechnern in absehbarer Zeit nicht zu schaffen.
P
P
Auf der anderen Seite ist es für heutige Computer recht einfach, A x mod M zu
errechnen. Bleiben wir bei dem Beispiel 731 x mod 321611. Man erkennt sofort,
dass man natürlich nicht zuerst 731 2332 und danach den Rest bezüglich 321611
bestimmen kann. Wieviele Stellen hat denn 731 2332 ungefähr?
(Antwort: 4664.) Zum Glück kann man, sobald 731 x zu groß zu werden droht, mit
dem Rest bezüglich 321611 weiterrechnen. Man benötigt einen „Algorithmus“, der
dem Computer sagt, wie er vorzugehen hat. Wir werden später darauf
zurückkommen.
P
P
P
P
P
P
P
P
P
P
Halten wir fest: Mit A x mod M hat man eine „Einwegfunktion“ erhalten. A x mod M
kann man auch bei großen Zahlen relativ leicht errechnen, aber aus dem Ergebnis
von A x mod M kann man nur sehr schwer oder überhaupt nicht auf x schließen.
P
P
P
P
P
P
Diffie und Hellman hatten 1976 (genau vor 30 Jahren!) die zündende Idee, wie man
mit einer solchen Funktion die oben erwähnte Kiste mit den beiden Schlössern
simulieren kann:
92
HBS Kryptologie
Im Tauschverzeichnis findet man eine exe-Datei namens Einwegfunktion. Kopiere
die Datei in Dein Homeverzeichnis und starte sie durch Doppelklick:
Zuerst soll mit dem Programm die Idee, die Diffie und Hellman hatten, erläutert
werden. Danach werden wir uns den Quelltext etwas genauer ansehen, um den
oben erwähnten Algorithmus kennenzulernen, mit dessen Hilfe man auch bei großen
Zahlen A x mod M errechnen kann.
P
P
Ganz oben ist die Einwegfunktion angegeben. Sie ist öffentlich und wir gehen davon
aus, dass sie Eve bekannt ist.
Alice und Bob haben einen privaten Schlüssel namens A und B
Mit der Einwegfunktion und ihrem privaten Schlüssel berechnen sie α und β . Dann
setzten sie sich ans Telephon und geben sich ihre Resultate an.
Wir gehen davon aus, dass Eva das Telephon abhört und so auch die Ergebnisse α
und β kennt.
Aufgabe 3:
Spiele die Situation mit selbst gewählten Zahle durch.
Was fällt auf, wenn man bei Alice und Bob auf „Schlüssel berechnen“ drückt?
Ist das immer so?
Wenn man davon ausgeht, dass die Zahlen sehr groß sind: Kann Eve durch die
öffentlichen und abgehörten Informationen auch auf den Schlüssel kommen?
Weshalb nicht?
Teste mit kleinen Zahlen per Hand, dass Alice und Bob das selbe Ergebnis
bekommen.
93
HBS Kryptologie
Es ist nicht schwer, zu beweisen, dass gilt:
((a x ) mod b) y ) mod b = ((a y ) mod b) x ) mod b
Sieht kompliziert aus, wenn man aber unsere konkreten Zahlen einsetzt, dann wirkt
die Formel schon etwas freundlicher:
((17 7 ) mod 91)11 ) mod 91 = ((1711 ) mod 91) 7 ) mod 91
Der obige Satz besagt, dass die Reihenfolge, in der die Einwegfunktion angewandt
wird, für das Ergebnis keine Rolle spielt. Und genau das haben wir bei obigem zweiSchlösser-Vergleich gesucht.
Weshalb nutzen Eve die abgehörten Ergebnisse nichts?
Wer dies noch immer nicht so recht versteht, dem nützt vielleicht der von Simon Sing
(„Geheime Botschaften“) stammende Vergleich:
Alice, Bob und Eve haben alle einen Eimer gelber Farbe. Alice und Bob haben
zusätzlich je eine weitere (Geheim-) Farbe, die nur sie selbst kennen.
Beide rühren einen bestimmten Teil ihre Farbe zur gelben Farbe dazu und schicken
sich die so gemischten Eimer.
Alice hat dann den Eimer von Bob. Zu der Mischfarbe rührt sie den Rest ihre eigene
Farbe dazu. Bob verfährt entsprechend.
Die so erzeugt Mischfarbe (der „gemeinsame Schlüssel“) ist bei beiden gleich.
Eve kann unmöglich ebenfalls auf diese Mischfarbe kommen, da sie aus den
abgefangenen Farbeimer die Geheimfarbe von Alice und Bob nicht extrahieren kann.
Eine einmal zusammengerührte Farbe lässt sich eben nicht wieder in ihre
Bestandteile zerlegen.
Um in der Analogie zu bleiben: „Farben zusammenrühren“ ist eine Einwegfunktion!
Nun zu obigem Programm:
Im Tauschverzeichnis befindet sich der Torso des Quelltextes.
Kopiere den Ordner Einwegfunktion_Huelle in das Homeverzeichnis und öffne dann
die Datei project1.dpr mit Delphi.
Wie man sieht ist die Oberfläche soweit in Ordnung und auch die
Ereignisbehandlungs-Routinen, wie TForme1.Activate oder TForm1.Button3.Click
sind fertig gestellt. Hierbei gibt es für uns nicht viel zu lernen, da sich das Programm
bis hier hin wie alle anderen, die wir vorher erstellt haben, programmieren lässt.
Unter private wurde eine Funktion namens einweg deklariert:
function einweg(m:int64; x:integer; modulo:integer):int64;
Das scheint vernünftig zu sein, da man ja immer wieder Terme, wie (17 9 ) mod 11
berechnen muss. Hier wäre m:= 17, x:= 9 und modulo:= 11.
P
P
P
P
Wie kann man den obigen Term möglichst einfach berechnen? Mit Sicherheit nicht
dadurch, dass man 17 9 zuerst ausrechnet!
Der Einfachheit halber soll die Funktion einweg(17,9,11) da der Modulowert 11 im
Folgenden fest bleibt, als f(17 9 ) dargestellt werden.
P
P
P
P
94
HBS Kryptologie
Aufgabe 4:
Überzeuge Dich, dass gilt:
1. f (17 9 ) = f (17 ⋅ 17 8 ) = f (f (171 ) ⋅ f (17 8 ))
2. f ((17 4 ) 2 ) = f (f (17 4 ) ⋅ f (17 4 )) = f (f (17 4 ) 2 )
Damit wird der Algorithmus, mit dem man f(17 9 ) berechnen kann, klar:
• Wenn der Exponent von 17 ungerade ist, dann zerlege wie oben in
Aufgabe 4.1
• Wenn der Exponent von 17 gerade ist, dann zerlege wie oben in
Aufgabe 4.2
P
P
Was man aus Aufgabe 4 noch erkennen kann ist, dass die Funktion f sich immer
wieder selbst aufruft, anders ausgedrückt: Beim Berechnen von f(17 9 ) wird f(17 1 ) und
f(17 8 ) aufgerufen. Der Vorgang wird solange durchgeführt, bis nur noch f(17 1 )
übrigbleibt.
Man nennt ein solches Vorgehen „Rekusion“. Auch wenn man anfangs einige Mühe
hat, den Vorgang zu durchschauen, so ist das Programmieren der rekursiven
Funktion einweg ausgesprochen einfach:
P
P
P
P
P
P
P
P
function TForm1.einweg(m:int64; x:integer; modulo:integer):int64; // rekursiv
begin
if x = 0 then result:= 1 else
if odd(x) then result:= m*(einweg(m,x-1,modulo)) mod modulo // m^x=m*m^(x-1)
else result:= (einweg(m*m mod modulo,x div 2,modulo)) mod modulo
//m^x=(m*m)^2
end;
Erläuterung:
odd(x) hat den (boolschen) Wert “true”, falls x ungerade ist.
Asymmetrische Schlüssel
Auch wenn die oben beschriebene Methode zur sichern Schlüsselübergabe (ohne
persönlichen Kontakt) ein Meilenstein in der Kryptologie darstellt, eine neue Methode
zur Verschlüsselung selbst ist damit nicht beschrieben. Woran liegt das?
Gehen wir das oben beschriebene Beispiel für Alice und Bob nochmals durch:
Alice wählt eine zufällige Zahl, Bob wählt eine zufällige Zahl. Sie haben keinen Text
vor sich, den sie verschlüsseln wollen. Sie wählen zufällige Zahlen und bekommten,
nachdem sie Informationen ausgetauscht haben, abhörsicher eine zufällige Reihe
95
HBS Kryptologie
von Buchstaben, die allenfalls für einen Schlüssel einer anderen Verschlüsselungsmethode dienen. Auch der notwendige Kontakt in beide Richtungen ist alles andere
als bequem.
Ohne die Leistung von Hellman und Diffie schmälern zu wollen: Ein neues
Verschlüsselungssystem haben sie nicht erfunden. Diffie muss gespürt haben, dass
er ganz nahe an der Lösung war. Er forschte ein weiteres Jahr, ob sich ihr Verfahren
nicht doch noch zu einem abhörsicheren Verschlüsselungssystem erweitern lassen
würde.
Dabei kam ihm der entscheidende Gedanke, dass ein vorheriger Kontakt (zum
Austausch der abhörsicheren Informationen) nicht nötig ist, wenn die Person, der er
eine Nachricht schicken will, einen Schlüssel veröffentlicht hat. Mit diesem ließe
sich dann die Nachricht verschlüsseln. Allerdings sollte es sich auch hier um eine
Einwegfunktion handeln. Mit anderen Worten: Nur der Empfänger ist in der Lage mit
seinem persönlichen Schlüssel die Nachricht auch wieder zu entschlüsseln.
In unserm Bild mit Kiste und Vorhängeschlössern würde das so aussehen:
Man ermöglicht auf irgend eine Weise, dass jeder ganz leicht ein offenes Vorhängeschloss des Empfängers besorgen kann. Für den Absender ist es ganz leicht, das
Vorhängeschloss einklicken zu lassen.Ebenso leicht ist es für den Empfänger, die
Kiste zu öffenen, da er als einziger den Schlüssel zu seinem Schloss hat. Der einzig
Enttäuschte dabei ist der neugierige Postbote.. .
Diffie war der Gedanke so wichtig, dass er ihn publizierte, - ohne die nötige
Einwegfunktion präsentieren zu können. Ein taktischer Fehler, wie sich herausstellen
sollte. Dennoch: Damit war die Idee der asymmetrischen Schlüssel geboren.
Wie kann man sich die Realisierung des Verfahrens vorstellen? Hier eine
Zusammenfassung der Anforderungen:
Asymmetrische Verschlüsselungsverfahren verwenden zwei unterschiedliche
Schlüssel K1 und K2, die folgenden Bedingungen genügen müssen:
•
•
K1 wird (nur) zum Verschlüsseln eines Klartextes N verwendet; im folgenden
wird der durch K1 erzeugte Chiffretext mit K1 [N] bezeichnet.
K2 wird (nur) zum Entschlüsseln eines Chiffretextes K1 [N] verwendet; damit
ist K2 [K1 [N]] = N.
Trotz der ersten beiden Bedingungen kann K2 nicht aus K1 (bzw. nur mit sehr hohem
Aufwand) hergeleitet werden.
Im Bild:
Bleibt die Frage offen: Wo soll man seinen öffentlichen Schlüssel ablegen?
Heute gibt es sogenannte Trust-Center, bei denen man den Schlüssel hinterlegen
kann. Von dort kann sie im Prinzip jeder abfragen.
Ist das denn sicher genug?
Überlege, was passiert, wenn man vom Trust-Center einen falschen Schlüssel erhält!
96
HBS Kryptologie
Nachdem Diffie die Idee des asymmetrischen Schlüssels vorgestellt und als Patent
angemeldet hatte, begann die Suche nach einer tauglichen Einwegfunktion.
(Kleine Ergänzung: Das Patent ist inzwischen abgelaufen!)
Man kann heute kaum mehr rekonstruieren, wie viele Wissenschaftler sich an der
Suche beteiligt haben. Fest steht aber, dass es Ronald Rivest gelang, eine solche
Funktion zu finden. Rivest war Informatiker und arbeitete im MIT-Labor für Informatik
in Massachusetts.
.
(Shamir, Rivest, Adleman)
Zusammen mit seinen Kollegen Adi Shamir hatten er ein Jahr lang alle erdenklichen
Funktionen vorgeschlagen. Leonard Adleman, ein Mathematiker, war der Dritte im
Bunde. Er hatte die Vorschläge seiner Kollegen auf mathematische Hieb-und
Stichfestigkeit zu überprüfen.
Dass die Primzahlen bei der gesuchten Einwegfunktion eine wichtige Rolle spielen
würden, war wohl Allen klar. Im Jahr 1977 hatte Rivest die zündende Idee. Kollege
Adelman konnte keinen Fehler finden und so wollte Rivest die Funktion unter den
alphabetisch angeordneten Namen Adleman, Rivest und Shamir veröffentlichen.
Sicherheitshalber fragte er seine Kollegen, ob sie mit der Veröffentlichung
einverstanden wären. Adelman fand, über den Grund kann man nur spekulieren,
dass sein Name nicht in der Autorenliste erscheinen sollte. War es falsche
Bescheidenheit oder eher die Befürchtung, dass die „Entdeckung“ unbedeutend sein
könnte? Fest steht ledigliche, dass Rivest Adelman überredete, dass der Name
Adelman wenigstens an letzter Stelle der Autorenliste erscheinen sollte:
Rivest, Shamir, Adelman, kurz: RSA.
Etwas Mathematik...
Bevor die Idee von Rivest dargelegt und programmiert werden kann, müssen einige
mathematische Voraussetzungen dargestellt werden. Beginnen wir beim dem noch
97
HBS Kryptologie
sehr einfachen Problem des ggT (größter gemeinsamer Teiler). Schon in Klasse 6
beschäftigen sich die Schüler mit der Frage: Was ist der ggT von 12 und 18? In
Kurzschreibweise: ggT(18,12)= ?? Richtig! ggT(18,12)= 6.
Für die Berechnung des ggT zweier Zahlen a und b gibt es einen sehr einfachen
Algorithmus:
ggT(18,12) = ggT(12, 18 mod 12) = ggT(12,6) = ggT(6, 12 mod 6) = ggT(6,0) = 6
Aufgabe 5:
Beweise, dass für alle natürlichen Zahlen a und b gilt: ggT(a,b) = ggT(b, a mod b).
Die obige Behauptung heißt übrigens Euklidischer Algorithmus! (Lösung S.162)
Siehe auch http://de.wikipedia.org/wiki/Euklidischer_Algorithmus
HTU
UTH
Danach ist eine (rekursives) Function in Delphi schnell geschrieben:
function TForm1.ggT(a,b:int64):int64;
begin
if b= 0 then result:=a
else
result:= ggT(b,a mod b)
end;
Leider sind wir noch nicht am Ende, was die mathematischen Voraussetzungen für
RSA angeht. Man benötigt für die Entschlüsselung mit dem privaten Schlüssel eine
zur zum öffentlichen Schlüssel „e“ inversen Schlüssel „d“, sodass gilt e*d=1 mod m.
Man beachte: m ist nicht öffentlich und wird mit m = (p-1)*(q-1) berechnet.
Mit dem erweiterten euklidischen Algorithmus findet man nicht nur den ggT zweier
Zahlen a und b, man kann sogar zwei passende Zahlen u und v finden, sodass gilt:
Erweiterter euklidischer Algorithmus:
Ist x = ggT(a,b), dann gibt es zwei ganze Zahlen u und v, so dass gilt:
u*a + v*w = x
Für RSA ist nur der Fall x = 1 interessant.
Ein Beispiel: ggT(44,35) = 1.
Durch probieren findet man 4*44 + (-5)*35 = 1 , also u = 4 und v = -5.
98
HBS Kryptologie
Wenn die Zahlen größer werden, dann ist die „Probiermethode“ nicht sehr sinnvoll.
Man benötigt daher, wie beim gewöhnlichen euklidischen Algorithmus ein Verfahren,
das uns „algorithmisch“ zum Ziel führt.
Da das Verfahren am besten erkannbar wird, wenn man den Beweis notiert, werden
wir genau dies jetzt tun:
Beweis für den erweiterten euklidischen Algorithmus:
Seien a
0,
B
B
b
und 1 = ggT(a, b) = ggT(b, a mod b) mit der Darstellung
1 = u'b + v'(a mod b)
Zwischenbehauptung: Es gibt u,v
mit: 1 = ua + vb
Setze u = v' und v = u' – v'(a div b) dann gilt:
1 = ggT(a,b) = u'b + v'(a mod b) = u'b + v'(a – (a div b)·b) = u'b + v'a – v'(a div b)·b
= v'a + (u' – v'(a div b))·b = ua + vb
(Die Operation div bezeichnet die ganzzahlige Division, z.B. 17 div 5 = 3).
Jetzt wird klar, wie der Algorithmus aussehen muss. Auch das ist ein klarer Fall für
eine rekursiv formulierte function:
function TForm1.ggT_erw(a,b:int64; var u,v:int64):int64;
var u0, v0: int64;
begin
if b= 0 then
begin
result:=a;u:=1; v:=0;
end
else
begin
result:= ggT_erw(b,a mod b,u0,v0);
u:= v0; v:= u0-(a div b)*v0;
end;
end;
Und noch etwas Zahlentheorie...
Zunächst sei eine Funktion j(n) für eine natürliche Zahl n definiert als die Anzahl der
zu n teilerfremden natürlichen Zahlen, die nicht größer als n sind. Dann gilt zum
Beispiel j(14) = 6, da 1, 3, 5, 9, 11 und 13 keinen gemeinsamen Teiler mit 14 haben.
99
HBS Kryptologie
Falls n eine Primzahl ist, ist j(n) = n-1 einfach zu berechnen, da alle natürlichen
Zahlen kleiner n keinen gemeinsamen Teiler größer 1 mit n haben.
Wenn n = pq mit p,q sind prim, dann ist j(n) = (p-1)(q-1) =: m.
Ist n keine Primzahl und sind die Primfaktoren von n nicht bekannt, so kann j(n) für
große n nur mit unzumutbarem Aufwand berechnet werden.
Bereits im 18. Jahrhundert bewies der Schweizer Mathematiker Euler einen Satz,
der erst durch das RSA-Verfahren eine praktische Anwendung gefunden hat. Dieser
Satz besagt, dass für zwei teilerfremde natürliche Zahlen n und b gilt:
b
j(n)
P
P
mod n = 1 , im Spezialfall n=pq also: b m mod n =1
P
P
Wird diese Gleichung mit k potenziert und anschließend mit b multipliziert, so ergibt
sich für b < n:
b km mod n = b
P
P
Mit dem obigen erweiterten euklidischen Algorithmus gibt es für eine zufällig
gewählte Zahl e mit ggT(e, m) = 1 eine Zahl d und eine Zahl v mit
de + vm = ggT(e,m) = 1, also k m + 1 = e · d
(1)
Auf diese Weise erhält man ergibt eine Formel, die als Verschlüsselungsalgorithmus
angewandt werden kann:
b ed mod n = (b e mod n) d mod n = b
P
P
P
P
P
P
Wird b als ein Teil der zu verschlüsselnden Nachricht, e und n als öffentliche und d
als geheimer Schlüssel interpretiert, so ist
c = b e mod n die Verschlüsselungsfunktion und
b = c d mod n die Entschlüsselungsfunktion.
P
P
P
P
Jetzt bleibt nur noch die Berechnung der Schlüssel n, e und d.
Setzt sich, wie hier vorausgesetzt die Zahl n aus zwei verschiedenen zufälligen
Primzahlen p und q zusammen (n = p · q), so ist j(n) = ( p - 1 ) ( q - 1 ) = m sehr
leicht zu berechnen, wenn p und q bekannt sind.
Falls p und q nach der Schlüsselerzeugung nicht mehr bekannt sind, kann m nur
noch mit großem Aufwand errechnet werden: Da n keine Primzahl ist, kann man
entweder alle Zahlen kleiner n auf gemeinsame Teiler mit n prüfen (was bei
genügend hohen Zahlen selbst für Großrechner zu aufwendig ist), oder man versucht
n in seine Faktoren zu zerlegen, was bei geeigneter Wahl von p und q ebenfalls sehr
aufwendig ist.
Damit die ganze Rechnung etwas übersichtlicher wird, schreiben wir ein Programm,
das bei Vorgabe zweier Primzahlen p und q den öffentlichen Schlüssel n und den
privaten Schlüssel m berechnet.
Danach soll man eine Zahl e wählen können, für die gilt: ggT(e,m) = 1.
100
HBS Kryptologie
Mit den vorhandenen Werten soll dann d berechnet werden, mit dessen Hilfe der
Empfänger die Nachricht wieder entschlüsseln kann.
Hier ein Ausschnitt aus dem Quelltext des Programms:
function TForm1.RSA_invers(e,m:int64):int64;
var d,s: int64;
begin
ggT_erw(e,m,d,s);
if d<0 then d:=d + m;
result:= d;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
p:= strtoint64(edit1.Text); q:= strtoint64(edit2.Text);
n:= p*q; m:=(p-1)*(q-1);
edit3.Text:= inttostr(n); edit4.Text:= inttostr(m);
label6.Visible:=true; label6.Caption:= 'n:= '+ inttostr(n);
label7.Visible:=true; label7.Caption:= 'm:= '+ inttostr(m);
label8.Visible:=true; label9.Visible:=true; edit5.Visible:=true;
button2.Visible:=true;
end;
101
HBS Kryptologie
procedure TForm1.Button2Click(Sender: TObject);
begin
e:= strtoint64(edit5.Text);
if ggT(e,m)=1 then
begin
label13.Visible:= true ;
label13.caption:='"e" ist in Ordnung!';
label11.Visible:=true; label12.Visible:=true;
button3.Visible:=true; label10.Visible:=true;
edit6.Visible:=true;
end
else
begin
label13.Visible:= true ;
label13.caption:='Bitte ein anderes "e" wählen!';
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
d:= RSA_invers(e,m);
edit6.Text:= inttostr(d);
end;
procedure TForm1.neueRechnung1Click(Sender: TObject);
begin
label6.visible:=false; label7.visible:=false;
label8.visible:=false; label9.visible:=false;
e:=0;d:=0;edit5.Visible:=false;Button2.visible:=false;
label11.visible:=false; label12.visible:=false;
label13.visible:=false;
edit3.Text:='';edit4.Text:='';
button3.visible:=false;
edit6.Visible:=false; label10.Visible:=false;
edit1.SetFocus;
end;
Aufgabe 6:
Versuche mit Hilfe des Quelltextes den Ablauf und die Logik des Programms zu
verstehen und programmiere danach ein Programm „RSA invers“, das die
beschriebenen Funktionen hat.
102
HBS Kryptologie
Lösung Aufgabe 5:
Man zeigt zunächst
Hilfssatz: Seien a
1. d | a
2. d | b
0,
B
B
b
. Dann gilt für alle d
d|b
d | (a mod b)
d | (a mod b)
d|a
sowie
Beweis:
1. Es sei a mod b = r. Dann ist a – r durch b teilbar. Also ist a – r auch durch d
teilbar. Da a durch d teilbar ist, muss auch r durch d teilbar sein, also gilt d | (a
mod b).
2. Wiederum sei a mod b = r. Dann ist a – r durch b und damit durch d teilbar. Da
r durch d teilbar ist, muss auch a durch d teilbar sein, d.h. es gilt d | a.
Und nun zum
Satz: Seien a
0,
B
B
b
. Dann gilt
ggT(a, b) = ggT(b, a mod b)
Beweis: Es wird gezeigt, dass ggT(a, b) und ggT(b, a mod b) sich gegenseitig teilen
und daher gleich sein müssen.
Sei d = ggT(a, b). Dann gilt
d | a und d | b
und nach dem Hilfssatz
d | (a mod b)
Nach Definition des ggt gilt
d|b
d | (a mod b)
d | ggT(b, a mod b)
Sei nun umgekehrt d = ggT(b, a mod b). Dann gilt
d | b und d | (a mod b)
und nach dem Hilfssatz
d|a
Nach Definition des ggT gilt
d|a
d|b
d | ggT(a, b)
qed.
103
HBS Kryptologie
Verschlüsseln und Entschlüsseln mit RSA
Nun sind alle Voraussetzungen geklärt und man kann endlich an der Umsetzung der
Idee arbeiten.
Gleich an dieser Stelle soll klargestellt werden, dass unser mit Delphi geschriebenes
Programm, keinesfalls heutigen Sicherheitsansprüchen genügen wird. Es soll
lediglich die Idee, die hinter RSA steht, transparent machen.
Woran liegt das? Wie wir schon mehrfach gesehen haben, kennt Delphi als größte
Integerwerte int64. Und das sind, wie der Name schon sagt, eben genau 64 Bit.
64 Bit aber heißt maximal 18 Stellen für n. Eine 18-stellige Zahl, die aus zwei
Primzahlen pq zusammengesetzt ist, zerlegt ein heutiger Computer in wenigen
Sekunden!
Damit man sich wirklich sicher fühlen kann, sollte n 512 Bit (ca. 154 Stellen) haben!
Das bedeutet, dass die Primzahlen p und q knapp 80 Stellen haben müssen (n = pq).
Es ist allerdings deutlich einfacher, bei einer solch großen Zahl p und q nachzuweisen, dass sie (sehr wahrscheinlich!) prim sind, als ihre Zerlegung zu finden.
Natürlich bekommt man Delphi auch dazu, mit viel größeren Werten zu rechnen. Das
ist dann allerdings etwas mehr Aufwand.....
Die Situtation:
Alice will Bob eine
Nachricht schicken
und verwendet
dessen öffentliche
Schlüssel e und n.
Eine Gefahr besteht
darin, dass Alice
einen Schlüssel
bekommt, der ihr von
Eve untergeschoben
wurde. Da Eve dann
natürlich auch den
privaten Schlüssel
hat, könnte sie die
Nachricht abfangen.
Um derartige
Probleme zu
vermeiden, hat man
die schon erwähnten
Trust-Center
eingerichtet.
Frage: Wie könnten
Bob und Alice ihre
Schlüssel gefahrlos
austauschen?
104
HBS Kryptologie
Das Verschlüsselungsprogramm zu schreiben, ist nun nach all den Vorbereitungen
nicht mehr schwer.
Hier ein Teil des Quelltextes:
function TForm1.InASCIIundVerschl(Zeile:string):string;
var i,L,Zahl1,Zahl2,LM: word; Zweierblock: cardinal;
HS,Mo:string;
begin
result:=''; HS:='';
L:=length(Zeile);
Mo:=inttostr(n); // Anzahl der Dezimalstellen von n
LM:=length(Mo);
i:=1;
while i<=L do
begin
Zahl1:=ord(Zeile[i]);
Zahl2:=ord(Zeile[i+1]);
Zweierblock:=Zahl2*256+Zahl1;
Zweierblock:= einweg(Zweierblock,e,n);
str(Zweierblock:LM,HS); // Anzahl der Zeichen von HS: LM
result:=result+ HS;
i:=i+2;
end;
L:=length(result);
for i:=1 to L do
begin
if result[i]=' ' then result[i]:='0'; //die leeren Stellen werden mit 0 aufgefüllt.
end;
end;
procedure TForm1.verschluesseln1Click(Sender: TObject);
var I,J : Integer; Zeile: string;
begin
e:=strtoint64(edit2.Text);
n:=strtoint64(edit1.Text);
j:= Memo1.Lines.Count;
for i:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
Memo2.Lines[I]:=InASCIIundVerschl(Zeile);
end;
end;
Aufgabe 7:
Versuche den Quelltext nachzuvollziehen und schreibe danach das Programm
“Verschlüsseln” selbst.
Einige Hinweise, um die Arbeit zu erleichtern:
Wenn n zum Beispiel 12 Stellen hat, dann kann bei der Verschlüsselung der
Zahlenwert b e mod n durchaus ebenfalls 12 Stellen haben.
P
P
105
HBS Kryptologie
b ist hier der Zahlenwert, der bei der Umwandlung zweier Buchstaben entsteht.
Gewöhnlich wird für e aber selten ein großer Wert verwendet, da die Rechnung sonst
unnötig aufwändig wird, die Sicherheit des Verfahrens aber von einem großen e nicht
profitiert.
Um nun bei großem n nicht dauernd unzählige Nullen an den Anfang stellen zu
müssen, verwendet man nicht zwei Zeichen in einem Block, um b zu berechenen,
sondern vielleicht 4, 8, 16, 32 oder gar 64 Zeichen.
Und nun bekommt Bob die verschlüsselte Nachricht von Alice, die aus lauter Ziffern
besteht. Zwar besitzt Bob den privaten Schlüssel m = (p-1)(q-1), er benötigt jedoch
zum Entschlüsseln das oben beschriebene d. Wie er dieses d aus e und m
bestimmt, haben wir weiter oben schon dargelegt.
Interessanter ist der Befehl „entschlüsseln“.
Wir wissen ja schon, dass wir die Zahlen in solche Blöcke einteilen müssen, die die
gleiche Länge haben, wie n Ziffern besitzt. Im unten dargestellten Beispiel sind es
zum Beispiel 6-er Blöcke.
106
HBS Kryptologie
Dann ist wieder die Einwegfunktion dran: b = c d mod n
c ist der Zahlenblock und d übernimmt die Rolle von e.
P
P
Und hier ist der Quelltext in Auszügen:
function TForm1.EntschlUndInText(Zeile:string):string;
var L,Zahl1,Zahl2,LM: word; Zweierblock:cardinal;
HS,Mo:string; C1,C2: char; I,K:integer;
begin
L:=Length(Zeile);
Mo:=inttostr(n);LM:=Length(Mo);
result:=''; HS:=''; I:=-LM+1;
While I<=L-LM do
begin
I:=I+LM;
HS:=Copy(Zeile,I,LM); //LM-lange Teilstrings werden hier von Zeile auf HS kopiert
Val(HS,Zweierblock,K);
Zweierblock:=einweg(Zweierblock,d,n);
Zahl2:=Zweierblock div 256;
Zahl1:=Zweierblock mod 256;
C1:=chr(Zahl1);C2:=chr(Zahl2);
result:=result+C1+C2;
end; {for L}
end;
procedure TForm1.dfrEntschlsselungberechnen1Click(Sender: TObject);
begin
e:= strtoint64(edit2.Text);
m:= (strtoint64(edit1.Text)-1)*(strtoint64(edit3.Text)-1);
n:= strtoint64(edit1.Text)*strtoint64(edit3.Text);
d:=RSA_invers(e,m);
label4.Caption:='d= '; edit3.text:=inttostr(d);
end;
procedure TForm1.entschlsseln1Click(Sender: TObject);
var I,J : Integer; Zeile: string;
begin
j:= Memo1.Lines.Count;
for i:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
Memo2.Lines[I]:=EntschlUndInText(Zeile);
end;
end;
Um den Vorgang des Verschlüsselns und des Entschlüsselns mit RSA etwas
transparenter zu machen, werden wir ein einfaches Beispiel mit kleinem Schlüssel e
Schritt für Schritt von Hand durchrechnen.
107
HBS Kryptologie
Mit dem Programm RSA-invers errechnen wir für den Fall p = 257 und q = 263 die
Schlüssel n und m. Dann wählen wir ein kleines e und lassen uns damit d errechnen:
In Kapitel 4 (S.91) haben wir
bereits die Umrechnung des
Wortes Ball durchgeführt:
Ba ll
= 24898 27756
Zunächst muss also
c = b e mod n berechnet
werden. Wir werden uns hier
nur um die Buchstaben Ba
kümmern.
P
P
c = 24898 3 mod 67591 =21352
P
P
(Man rechnet zuerst 23898 2
mod 67591 = 33343 und
multipliziert das Ergebnis mit
24898 und sehen uns das
Ergebnis mod 67591 an.)
P
P
Will man 21352 wieder
entschlüsseln, so muss man
b = c d mod n berechnen, also: b = 21352 44715 mod 67591. Leider ist d hier schon
so groß, dass man die Aufgabe dem Rechner übergeben muss.
Und der erhält b = 24898!!
P
P
P
P
Man mag hier kritisieren, dass für eine Rechnung per Hand, die Primzahlen p und q
viel zu groß gewählt wurden. Leider ist dies jedoch nicht zu vermeiden, wenn man
Blöcke aus zwei Buchstaben zusammenfassen will, denn man bekommt dann
256*256 = 65536 Möglichkeiten. Eine Rechnung mit b e mod 15 (p=3,q=5) kann aber
nur 15 verschiedene Ergebnisse haben.
Eine Möglichkeit wäre, nur Zeichen für Zeichen in ASCII zu verwandeln. Dann muss
n nur größer als 256 sein (z.B.: p= 17 und q= 19).
P
P
Im Moment ist das alles noch zu umständlich. Deshalb schreiben wir ein Programm,
dass Primzahlen p und q zufällig aussucht und den Benutzer (Bob) eine Zahl e
aussuchen lässt und gleich noch prüft, ob die Bedingung ggT(e,m) = 1 erfüllt ist.
Dann soll man zu Alice gehen können und ihr beim Verschlüsseln zusehen können.
Zum Schluss bekommt im letzten Fenster Bob die verschlüsselte Nachricht und
entschlüsselt den Text mit seinem privaten Schlüssel.
Das ganz kann dann vielleicht so aussehen:
108
HBS Kryptologie
109
HBS Kryptologie
Wir benötigen also 3 Formulare und 3 Units.
Unit1: (in den neuen Teilen)
function TForm1.PrimTest(zahl:int64): boolean ;
var k: longword;
begin
k:=2;
if zahl mod k = 0 then
begin result:=false; exit; end
else k:=3;
while k*k <= zahl do begin
if zahl mod k = 0 then
begin result:=false; exit; end //ist t nichprim
else k:=k+2;
end; //of while
result:=true;
end;
110
HBS Kryptologie
function TForm1.ggT(a,b:int64):int64;
begin
if b= 0 then result:=a
else
result:= ggT(b,a mod b)
end;
function TForm1.ggT_erw(a,b:int64; var u,v:int64):int64;
var u0, v0: int64;
begin
if b= 0 then
begin
result:=a;u:=1; v:=0;
end
else
begin
result:= ggT_erw(b,a mod b,u0,v0);
u:= v0; v:= u0-(a div b)*v0;
end;
end;
function TForm1.RSA_invers(e,m:int64):int64;
var d,s: int64;
begin
ggT_erw(e,m,d,s);
if d<0 then d:=d + m;
result:= d;
end;
procedure TForm1.pwhlen1Click(Sender: TObject);
var Test:boolean ;
begin
randomize;
Test:=false;
while Test=false do
begin
p:=random(100000); //mit größeren p und q wird im Folgenden der
Test:=PrimTest(p); //Bereich von int64 verlassen
end;
edit1.Text:=inttostr(p);
end;
procedure TForm1.qwhlen1Click(Sender: TObject);
var Test:boolean ;
begin
randomize;
Test:=false;
while Test=false do
begin
q:=random(100000); //s.o.
Test:=PrimTest(q);
111
HBS Kryptologie
end;
edit2.Text:=inttostr(q);
edit3.visible:=true;edit4.visible:=true;
label3.visible:=true;label4.visible:=true;
n:=p*q; m:=(p-1)*(q-1) ;
edit3.Text:=inttostr(n); edit4.Text:=inttostr(m);
end;
procedure TForm1.e1Click(Sender: TObject);
begin
e:=random(100000);//e ist Exponent und muss nicht groß sein..
while ggT(e,m)<>1 do
begin
e:=random(100000);
end ;
edit5.visible:=true;label5.visible:=true;
edit5.text:=inttostr(e);
label7.Visible:= true;label8.Visible:= true;
end;
procedure TForm1.dbe1Click(Sender: TObject);
begin
label6.Visible:=true;label9.Visible:=true;
label10.Visible:=true; label11.Visible:=true;
edit6.Visible:=true;
d:= RSA_invers(e,m);
edit6.Text:= inttostr(d);
end;
procedure TForm1.ZuAlice1Click(Sender: TObject);
begin
Form2.edit1.Text:=inttostr(n);//Bob sendet die öffentlichen Schlüssel "n"
Form2.edit2.text:=inttostr(e);//und "e" an Alice (Form2)
//Bei der Gelegenheit speichert Bob
Form3.edit1.Text:= inttostr(d);//den errechnete geheime Schlüssel "d",
Form3.edit2.Text:=inttostr(m);//den geheime Schlüssel "m"
Form3.edit3.text:=inttostr(e);//den öffentl.Schlüssel "e" und
Form3.edit4.Text:=inttostr(n);//"n" auf seinem Computer.
Form2.Show;//Hier kann man Alice bei der Verschlüsselung zuschauen...
end;
112
HBS Kryptologie
Unit 2:
function TForm2.einweg(m:int64; e:int64; modulo:int64):int64; // rekursiv
begin
if e = 0 then result:= 1 else
if odd(e) then result:= m*(einweg(m,e-1,modulo)) mod modulo // m^e=m*m^(e-1)
else result:= (einweg(m*m mod modulo,e div 2,modulo)) mod modulo
//m^e=(m*m)^2
end;
function TForm2.InASCIIundVerschl(Zeile:string):string;
var i,L,Zahl1,Zahl2,LM: word; Zweierblock:cardinal;
HS,Mo:string;
begin
result:=''; HS:='';
L:=length(Zeile);Mo:=inttostr(n);
LM:=length(Mo);
i:=1;
while i<=L do
begin
Zahl1:=ord(Zeile[i]);
Zahl2:=ord(Zeile[i+1]);
Zweierblock:=Zahl2*256+Zahl1;
Zweierblock:= einweg(Zweierblock,e,n);
str(Zweierblock:LM,HS);
result:=result+ HS;
i:=i+2;
end;
L:=length(result);
for i:=1 to L do
begin
if result[i]=' ' then result[i]:='0';
end;
end;
procedure TForm2.verschluesseln1Click(Sender: TObject);
var I,J : Integer; Zeile: string;
begin
e:=strtoint64(edit2.Text);
n:=strtoint64(edit1.Text);
j:= Memo1.Lines.Count;
for i:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
Memo2.Lines[I]:=InASCIIundVerschl(Zeile);
Form3.Memo1.Lines[I]:=InASCIIundVerschl(Zeile);
end;
end;
procedure TForm2.zuBob1Click(Sender: TObject);
113
HBS Kryptologie
begin
Form3.show; //Hier kann man Bob beim Entschlüsseln beobachten..
end;
......................................................
procedure TForm2.ErstesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=0 to 500 do
begin
Memo1.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TForm2.ZweitesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=0 to 500 do
begin
Memo2.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TForm2.UnteresFeldhochkopieren1Click(Sender: TObject);
begin
Memo1.Text:= Memo2.Text;
end;
procedure TForm2.Beenden1Click(Sender: TObject);
begin
Form2.ZweitesFeldlschen1Click(Sender);
close;
end;
114
HBS Kryptologie
Unit 3:
function TForm3.EntschlUndInText(Zeile:string):string;
var L,Zahl1,Zahl2,LM: word; Zweierblock:cardinal;
HS,Mo:string; C1,C2: char; I,K:integer;
begin
L:=Length(Zeile);
d:=strtoint64(edit1.Text); m:= strtoint64(edit2.Text);
e:=strtoint64(edit3.Text); n:=strtoint64(edit4.Text);
Mo:=inttostr(n);LM:=Length(Mo);
result:=''; HS:=''; I:=-LM+1;
While I<=L-LM do
begin
I:=I+LM;
HS:=Copy(Zeile,I,LM);
Val(HS,Zweierblock,K);
Zweierblock:=einweg(Zweierblock,d,n);
Zahl2:=Zweierblock div 256;
Zahl1:=Zweierblock mod 256;
C1:=chr(Zahl1);C2:=chr(Zahl2);
result:=result+C1+C2;
end; {for L}
end;
function TForm3.ggT_erw(a,b:int64; var u,v:int64):int64;
var u0, v0: int64;
begin
if b= 0 then
begin
result:=a;u:=1; v:=0;
end
else
begin
result:= ggT_erw(b,a mod b,u0,v0);
u:= v0; v:= u0-(a div b)*v0;
end;
end;
function TForm3.einweg(m:int64; e:int64; modulo:int64):int64; // rekursiv
begin
if e = 0 then result:= 1 else
if odd(e) then result:= m*(einweg(m,e-1,modulo)) mod modulo // m^e=m*m^(e-1)
else result:= (einweg(m*m mod modulo,e div 2,modulo)) mod modulo
//m^e=(m*m)^2
end;
procedure TForm3.entschlsseln1Click(Sender: TObject);
var I,J : Integer; Zeile: string;
begin
115
HBS Kryptologie
j:= Memo1.Lines.Count;
for i:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
Memo2.Lines[I]:=EntschlUndInText(Zeile);
end;
end;
……………………………………………..
procedure TForm3.ErstesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=0 to 500 do
begin
Memo1.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TForm3.ZweitesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=0 to 500 do
begin
Memo2.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TForm3.UnteresFeldhochkopieren1Click(Sender: TObject);
begin
Memo1.Text:= Memo2.Text;
end;
procedure TForm3.Beenden1Click(Sender: TObject);
begin
Form3.ErstesFeldlschen1Click(Sender);
Form3.ZweitesFeldlschen1Click(Sender);
close;
end;
116
HBS Kryptologie
Sehr lange Zahlen (Very long integer: VLInt)
Bevor wir uns mit dem Wissen um Transnigma und RSA eine „ RSA-Transnigma“
basteln, wird es Zeit für eine ernüchternde Information:
Selbst ein schwacher Rechner benötigt für die Zerlegung einer 18-stelligen Zahl n
(und mehr können wir ja mit int64 nicht erreichen) in die zwei Primfaktoren p und q
kaum mehr als eine halbe Stunde!
Zwar ist mit den obigen Ausführungen das Prinzip von RSA nun (hoffentlich!) klar.
Auch wissen wir, wie man mit Delphi RSA Ver-und Entschlüsselungsprogramme
schreibt. Was nützen uns jedoch diese Kenntnisse, wenn ein Angriff auf die Methode
in wenigen Minuten zum Ziel führt?
Konsequenz: Wir benötigen in Delphi „lange Zahlen“ oder besser „sehr lange
Zahlen“. Was Delphi nicht selbst bietet (mit Einschränkungen maximal 64 Bit, also 19
Dezimalstellen), muss mit den vorhanden Strukturen selbst programmiert werden.
Sie kennen das von unzähligen functions und procedures, die Sie schon
geschrieben haben.
Eine erste Idee wäre, dem Prozessor genauer auf die „Register“ zu schauen. Wie
haben die Prozessoren die Erweiterung von 8 Bit auf 16 Bit und wie von 16 auf 32 Bit
geschafft? Wie macht es Delphi mit der Erweiterung von Integer auf int64?
Da wir diese Methode nicht wählen werden, ist es nicht sinnvoll, sehr viel Zeit auf
eine mögliche Implementation in Delphi zu verwenden.
Stellen Sie sich einfach vor, ein beliebiges Gerät kann mit 8 Bit-Zahlen rechnen. Für
das Addieren heißt das konkret, dass etwa das Ergebnis
10011011 + 00100110 = 11000001
berechnet werden kann. Hat man nun zwei 16-Bit-Zahlen zu addieren, so geht man
folgendermaßen vor: Teile die Zahl in zwei 8-Bit-Zahlen mit Namen Low und High.
Addiere zunächst die beiden Low-Zahlen. Bei einem Übertrag von 1 addiere zu einer
der High-Zahlen 1. Danach werden die High-Zahlen addiert. Fertig!
Ein Beispiel:
00101101 11000011
High
Low
+
01000110 11100000 (dezimal: 11715 + 18144)
High
Low
Aufgabe 8:
Berechnen Sie nach der obigen Angaben das Ergebnis der 16-Bit Addition!
Zur Erinnerung: Im Dualsystem ist 1 +1 = 10
(Ergebnis: 01110100 10100011 ; dezimal: 29 859)
Zugegebenermaßen wird die Sache bei der Multiplikation etwas komplizierter. Wenn
man sich aber an die Methode der schriftlichen Multiplikation erinnert, dann wird klar,
das auch die 16-Bit-Multiplikation durch mehrfachen 8-Bit-Multiplikation und
anschließender Addition (wie oben beschrieben) zu schaffen ist.
117
HBS Kryptologie
Schwierig scheint die Sache nun wirklich nicht zu sein. Weshalb suchen wir noch
nach besseren Methoden?
Gehen wir von int64-Zahlen aus, so hätten wir danach 128-Bit-Zahlen. Das sind 38
Dezimalstellen. Nicht gerade beeindruckend! Aber auch 256-Bit genügen heutigen
Anforderungen nicht mehr. Will man gar für die Zukunft gewappnet sein, so wären
mindestens 1024 Bit (308 Dezimalstellen) das Maß aller Dinge. Man müsste das
Verfahren daher mindestens vier Mal anwenden. So verliert eine anfangs gute Idee
an Attraktivität. Die Geschwindigkeit bei einer solchen Vierfach-Schachtelung wird
ebenfalls nicht berauschend sein.
(Bemerkung am Rande: Die int64 Nutzung ist in Delphi übrigens nicht vollständig
implementiert. Beispielsweise dürfen die Variablen einer for-Schleife keine int64Variablen sein.)
Für die Programmiersprachen C# und C++ findet man im Internet viele Ideen.
(Beispielsweise: http://www.codeproject.com/csharp/biginteger.asp )
Neben dem oben beschriebenen Verfahren sind sogenannte „Pointer“ hoch im Kurs.
Um zu verstehen, was Pointer (Zeiger) sind, lesen Sie den kurzen Text in Wikipedia:
http://de.wikibooks.org/wiki/Programmierkurs:_Delphi:_Pointer
HTU
UTH
HTU
UTH
Vereinfacht formuliert sind Pointer im Prinzip Integer-Zahlen, die auf bestimmte
Stellen im Speicher zeigen. Mit ihrer Hilfe ist es möglich, an den Inhalt der Stelle zu
kommen, auf die gezeigt wird.
In Delphi haben Pointer für den Programmierer eher eine untergeordnete Rolle, aber
Objekte und Strings sind auch hier eigentlich nur Pointer auf einen Bereich im
Speicher. Hierbei wird der Zugriff allerdings von Delphi verwaltet.
In anderen Sprachen, wie C# und C++, ist die Anwesenheit von Pointern viel
offensichtlicher. So ist es nicht verwunderlich, dass in diesen Programmiersprachen
viele Lösungen für VGInt im Internet kursieren, die auf der Pointer-Idee beruhen.
Vielleicht haben Sie sich verwundert die Augen gerieben, als man Ihnen Strings als
Pointer verkauft hat. Viel eher würde Ihnen ein String als Array of Char einleuchten.
Stimmt! Nur: Arrays sind auch nichts anderes als Pointer.....
Bei String-Variablen handelt es sich tatsächlich nur um Zeiger auf die Stellen im
Arbeitsspeicher. Der String kann somit (rein theoretisch) bis zu 2 GB groß werden,
wobei sich der Speicherverbrauch automatisch der String-Länge anpasst. Enthält der
String keine Zeichen, verbraucht er auch keinen Speicherplatz, abgesehen von 4
Byte für den Zeiger auf nil, also ins Leere.
Keine Sorge, wir werden nicht zu C# wechseln. (Auch nicht zu Java, wo man sich
über die Länge von Integervariable überhaupt keine Gedanken machen muss, denn
diese können dort –theoretisch- beliebig groß sein... )
Hilfe kommt von hier: http://www.gk-informatik.de/oop/vlint.html
Die hervoragenden Seiten stammen von Roland Mechling. Er behandelt das Thema
im Rahmen der OOP (objektorientierten Programmierung) in Delphi. Seine Idee ist,
TList (in Abwandlung: TVLInt) zu verwenden (bzw zu erzeugen).
TList ist ein dynamischer Array von Pointern. Mit ihm lassen sich mehrere Pointer
verwalten und sortieren. Mechling schreibt:
HTU
UTH
118
HBS Kryptologie
Der möglicherweise naheliegenden Versuchung, TVLInt gleich von TList abzuleiten,
sollte man widerstehen, weil dann automatisch das gesamte Interface von TList in
TVLInt veröffentlicht werden würde. Da wir uns jedoch stets um möglichst "kleine"
Schnittstellen bemühen wollen, ist es sinnvoller, die Liste als "protected"-Variable zu
deklarieren und sie nur intern zu verwenden.
Das allerdings ist auch nicht ganz trivial. Während nämlich eine normale Liste
irgendwelche Objekte (also Nachfahren von TObject) speichert, wollen wir hier nur
Integerwerte in der Liste ablegen, nämlich die einzelnen Stellen der Zahl. Zunächst
werden wir uns auf die dezimale Darstellung beschränken, damit wir es beim
Entwickeln etwas leichter haben. Jeder Listenplatz kann also einen der Werte {0, 1,
2, .... 8, 9} enthalten.
Recht hat er: Die Sache ist keinesfalls trivial. Es sei daher allen Interessierten
dringend angeraten, sein Kapitel 4 (in OOP) sorgfältig zu lesen und auch die
Übungen zu machen. Denn wir werden die Unit mVLInt für unsere Programme mit
„sehr großen Zahlen“ benutzen.
Wir wollen hier einen kleinen Teil (die Addition zweier VLI) seiner Unit etwas genauer
betrachten:
//--------- AddAbs (protected) ----------------------------------------procedure TVLInt.AddAbs(S2: TVLInt);
var carry,
// Übertrag
ssum,
// Stellensumme
gsz,
// größere (der beiden) Stellenzahl(en)
i : Integer; // Laufvariable
begin
carry := 0;
If Self.DigitCount > S2.DigitCount then
gsz := Self.DigitCount
else
gsz := S2.DigitCount;
For i := 0 to gsz - 1 do begin
ssum := Digit[i] + S2.Digit[i] + carry;
Digit[i] := ssum Mod 10;
carry := ssum Div 10;
end;
If carry > 0 then
Digit[gsz] := carry;
end;
.................................................................................................
//--------- Add (public) ----------------------------------------------procedure TVLInt.Add(S2: TVLInt);
var Pu : TVLInt;
begin
If IsNegativ = S2.IsNegativ then // Gleiche Vorzeichen
AddAbs(S2)
else
// Verschiedene Vorzeichen
Case Self.AbsCompareWith(S2) of
1 : SubtrAbs(S2);
0 : begin
FDigits.Count := 1;
FDigits.Items[0] := Nil;
119
HBS Kryptologie
FIsNegativ := False;
end;
-1 : begin
Pu := TVLInt.CreateCopyOf(S2);
Pu.SubtrAbs(Self);
Self.Assign(Pu);
Pu.Free;
end;
end;
end;
Im Teil AddAbs (protected) wird die Summe des Betrages (zweier Repräsentanten
von TVLInt ) berechnet. Also zum Beispiel abs(-4223) + abs(50998).
Dies ist so zu verstehen, dass der aufrufende Repräsentant (ohne Vorzeichen) mit
der Methode AddAbs den Repräsentanten S 2 (ebenfalls ohne Vorzeichen) zu sich
selbst dazu addiert.
Die Anzahl der „Dezimalstellen“ des größeren der beiden bestimmt gsz.
Dann werden einfach die Summen der „Dezimalstellen“ addiert und falls nötig ein
Übertrag in carry abgelegt. Dieser Übertrag wird dann in der nächst höheren
„Dezimalstelle“ berücksichtigt.
B
B
Im Teil Add (public) wird bei der Addition das Vorzeichen beachtet. Außerdem muss,
falls neuer Speicherplatz erforderlich ist (Pu := TVLInt.CreateCopyOf(S2);), dieser
auch wieder freigegeben werden (Pu.Free;).
Vielleicht ist Ihnen auch aufgefallen, dass der erste Summand nach der Anwendung
der Methode Add nicht mehr vorhanden ist, da er durch das Ergebnis der Summe
überschrieben wird.
Weshalb ist AddAbs als „protected“ erstellt worden? Zur Erinnerung:
Ein protected-Element ist wie ein private-Element innerhalb der gleichen Unit
verwendbar. Darüberhinaus haben alle abgeleiteten Klassen darauf Zugriff,
unabhängig davon, in welcher Unit sie sich befinden.
Mehr kann man eigentlich nicht verlangen. Zum Einen ist AddAbs gekapselt in der
eigenen Unit, kann also von außen nicht verändert werden. Zum Anderen haben alle
abgeleiteten Klassen darauf Zugriff, - egal in welcher Unit sie sich befinden.
Dieser kleine Ausflug in die Unit mTVLInt von Roland Mechling ist als Ansporn
gedacht, auch die anderen Teile genauer nachzuvollziehen.
Auch wenn Unit mVLInt unverändert zum „Aufpäppeln“ unserer bisherigen RSAProgramme übernommen werden wird, ihre Verwendung wird sich in diesem Skript
jedoch deutlich von der Methode, die bei Mechling in Kapitel 5 dargestellt wird,
unterscheiden.
Dafür gibt es einen einleuchtenden Grund: Unsere RSA-Programme sind im
Wesentlichen fertig und müssen lediglich mit anders formulierte Rechenoperationen
für VLInt ausgestattet werden.
Hat man bisher zum Beispiel geschrieben c:= a + b , so muss jetzt für das
Pluszeichen etwas Anderes stehen.
Versuchen Sie einmal, diese simple Addition in der Terminologie von mTVLint zu
erstellen! Sie werden feststellen, dass das nicht gerade übersichtlich ist. Außerdem
120
HBS Kryptologie
muss man sich auch noch im die Speicherverwaltung kümmern und eventuell Kopien
von den überschriebenen TVLInt erstellen.
Schöner wäre da ein Term wie: c:= Plus(a,b) oder für die Multiplikation c:=Mult(a,b).
Und genau das werden wir im nächsten Programm „Grosse Integer“ ausprobieren.
Es sollen dort die wichtigen Rechenoperationen mit diesen VLInt durchgeführt
werden.
(Die benötigte Unit mTVLInt, in der Repräsentanten von TVLInt erzeugt und mit
Attributen und Methoden, wie Addition, Multiplikation etc angereichert wird, ist im
Tauschverzeichnis abgelegt.)
Rechnen mit TVLInt
Hier ein Vorschlag für die Oberfläche:
Im Deklarationsteil der neuen Unit benötigen wir zwei Repräsentanten von TVLInt:
private
v1, v2 : TVLInt;
Die Unit soll später als Unit VLIrechnen in den Kryptologie-Programmen dienen.
Hier ein Beispiel für eine function für das Addieren:
function TForm1.Plus(z1,z2:string):string;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
v1.Add(v2);
result := v1.AsString;
v1.Free;
v1 := TVLInt.Create(z1);
end;
Darauf sollten Sie achten:
•
Als Varible werden nun Strings verwenden. Auch Strings sind nichts anderes
als Pointer. Durch die Methode „AsString“ in Unit mTVLInt ist die Verwandlung
kein Problem.
121
HBS Kryptologie
•
•
Die Speicherverwaltung wird in dieser function übernommen.
Der Repräsentant v1 wird bei der Addition überschrieben. Damit er nach der
Addition noch zur Verfügung steht, wird er mit TVLInt.Create(z1) neu erzeugt.
Mit dieser function kann man für die Addition zweier VLInt namens a und b nun
Plus(a,b) schreiben.
Aufgabe 9:
Schreiben Sie unter Verwendung von Unit mTVLInt functions für die Subtraktion
(Min), für die Multiplikation (Mult) und für Modulo (GMod).
Wie kann man eine Relation, wie „ist größer als“ in einer function formulieren?
Versuchen Sie es zunächst ohne die Lösung (nächste Seite) zu benutzen!
Jetzt muss noch geklärt werden, was zu passieren hat, wenn man beispielsweise auf
den „Additions-Button“ (BAdd) drückt:
procedure TForm1.BAddClick(Sender: TObject);
var z1,z2:string;
begin
z1:=edit1.Text; z2:=edit2.Text;
edit3.Text:= Plus(z1,z2);
end;
Einfacher geht es wirklich nicht!
Und hier ein Vorschlag für z1 > z2 (Name des Buttons: BGr):
procedure TForm1.BGrClick(Sender: TObject);
var z1,z2:string;
begin
z1:=edit1.Text; z2:=edit2.Text;
If Gr(z1,z2) = true then
Edit3.Text := 'Ja!'
else
Edit3.Text := 'Nein!';
end;
Lösung Aufgabe 9:
function TForm1.Min(z1,z2:string):string;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
122
HBS Kryptologie
v2 := TVLInt.Create(z2);
v1.Subtr(v2);
result := v1.AsString;
v1.Free;
v1 := TVLInt.Create(z1);
end;
function TForm1.Gdiv(z1,z2:string):string;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
v1.DivBy(v2);
result := v1.AsString;
v1.Free;
v1 := TVLInt.Create(z1);
end;
function TForm1.Gmod(z1,z2:string):string;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
v1.Modulo(v2);
result := v1.AsString;
v1.Free;
v1 := TVLInt.Create(z1);
end;
function TForm1.Mult(z1,z2:string):string;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
v1.Mult(v2);
result := v1.AsString;
v1.Free;
v1 := TVLInt.Create(z1);
end;
function TForm1.GrGl(z1,z2:string):boolean;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
If v1.IsGreaterOrEqualThan(v2) then
result := true
else
result := false;
v1.Free;
v1 := TVLInt.Create(z1);
123
HBS Kryptologie
end;
function TForm1.Gr(z1,z2:string):boolean;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
If v1.IsGreaterThan(v2) then
result := true
else
result := false;
v1.Free;
v1 := TVLInt.Create(z1);
end;
function TForm1.Gl(z1,z2:string):boolean;
begin
v1.Free;
v1 := TVLInt.Create(z1);
v2.Free;
v2 := TVLInt.Create(z2);
If v1.IsEqual(v2) then
result := true
else
result := false;
v1.Free;
v1 := TVLInt.Create(z1);
end;
function TForm1.Hoch(Z1:string;e: cardinal):string;
var i:cardinal;
begin
v1.Free;
v1 := TVLInt.Create(Edit1.Text);
v2.Free;
v2 := TVLInt.Create(Edit1.Text);
e:=strtoint(edit2.text);
for i:=1 to e-1 do
begin
v1.Mult(v2);
end;
result := v1.AsString;
v1.Free;
v1 := TVLInt.Create(Edit1.Text);
end;
Die übrigen benötigten Funktionen und Relationen, wie Ganzzahldivision oder
„größer-gleich“, sind ebenfalls in der Lösung zu finden.
Jetzt müssen nur noch die OnClick-Ergeignisse der übrigen Buttons festgelegt
werden und das Programm ist fertig.
124
HBS Kryptologie
Testen Sie Ihr Programm zum Beispiel durch Multiplikation und anschließende
Division.
Sobald das Programm vollständig ist, können wir an das Umschreiben der „alten“
Kryptologie-Programme gehen. Hier soll exemplarisch das Umschreiben von
„RSA_invers“ vorgeführt werden.
Die Oberfläche muss lediglich etwas vergrößert werden, um den nun deutlich
längeren Editfeldern genügend Platz zu lassen:
Im Deklarationsteil :
private
{ Private declarations }
p,q,n,m,e,d : string;
v1, v2 : TVLInt;
Die functions und procedures umschreiben:
function TForm1.ggT(a,b:string):string;
begin
if b= '0' then result:=a
else
result:= ggT(b,Gmod(a,b));
end;
function TForm1.ggT_erw(a,b:string; var u,v:string):string;
var u0, v0: string;
begin
if b= '0' then
begin
result:=a;u:='1'; v:='0';
end
else
begin
//früher:
result:= ggT_erw(b,Gmod(a,b),u0,v0); //result:=ggT_erw(b,a mod b,u0,v0);
u:= v0; v:= Min(u0,Mult((Gdiv(a,b)),v0));//v:= u0-(a div b)*v0
125
HBS Kryptologie
end;
end;
function TForm1.RSA_invers(e,m:string):string;
var d,s: string;
begin
ggT_erw(e,m,d,s);
if Gr('0',d) then d:=Plus(d,m);
result:= d;
end;
Die Buttons heißen von oben nach unten: Button1, Button2, Button3.
Das sind die zugehörigen OnClick-Ereignis-Prozeduren:
procedure TForm1.Button1Click(Sender: TObject);
begin
p:= edit1.Text; q:= edit2.Text;
n:= Mult(p,q); m:=Mult(Min(p,'1'),Min(q,'1'));
edit3.Text:=n; edit4.Text:=m;
label8.Visible:=true; label9.Visible:=true; edit5.Visible:=true;
button2.Visible:=true;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
e:=edit5.Text;
if ggT(e,m)='1' then
begin
label13.Visible:= true ;
label13.caption:='"e" ist in Ordnung!';
label11.Visible:=true; label12.Visible:=true;
button3.Visible:=true; label10.Visible:=true;
edit6.Visible:=true;
end
else
begin
label13.Visible:= true ;
label13.caption:='Bitte ein anderes "e" wählen!';
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
d:= RSA_invers(e,m);
edit6.Text:= d;
end;
Testen Sie das Programm mit den Primzahlen:
p = 1467716244772025206549628844202607691083
q = 2886249354184641066973295982037392165061
Verwenden Sie e = 17.
126
HBS Kryptologie
Dann sollte für d die folgende Zahl herauskommen:
2741067394093835738492212756463349600061296855570783589810250376312
058821996713
Rabin-Miller-Test
Bei der Bestimmung von großen Primzahlen müssen wir uns etwas Anderes einfallen
lassen. Zwar ist die Idee für den Primzahltest durch die Verwendung von VLInt nicht
falsch geworden, aber man kann sich unschwer vorstellen, dass ein Test aller
ungeraden Zahlen bis zur Wurzel der Zahl bei einer 100-stelligen Zahl einige Zeit
brauchen würde........ ;-)
Es ist zwar sehr schwierig, eine hundertstellige Zahl, die aus zwei Primfaktoren
besteht, zu zerlegen, verhältnismäßig einfach ist es jedoch von einer hundertstelligen
Zahl zu sagen, ob sie (vermutlich) prim ist oder nicht.
Dazu benötigt man ein Verfahren, das völlig anders als etwas das „Sieb des
Eratosthenes“ abläuft. Ein solches Verfahren ist der Rabin-Miller-Test. Er wurde von
Garry Miller und Michael Rabin 1976 entwickelt.
Wenn er zum Ergebnis kommt, dass die getestete Zahl nicht prim ist, so ist die
Aussage (keine Rechenfehler vorausgesetzt) mit Sicherheit richtig.
Kommt er aber zum Ergebnis, dass die getestete Zahl vermutlich prim ist, so irrt der
Test in 25% der Fälle.
Für Nichtmathematiker ist es kaum erklärlich, weshalb der Test funktioniert. Aber
keine Sorge: Der Test wird jetzt zunächst sauber formuliert und danach soll versucht
werden, ihn einigermaßen plausibel zu machen.
Wer den Beweis genau durchdenken will, kann das sehr anschlaulich gehaltene
Skript von Florian Rienhardt verwenden: (Auch im Tauschverzeichnis)
http://www.bitnuts.de/rienhardt/docs/miller_rabin.pdf
HTU
UTH
Rabin-Miller-Test für die Zahl p:
1. Wähle zufällig eine Zahl a < p.
Da p ungerade gibt es natürliche Zahlen b und m, so dass p = 2 b m +1.
(b soll die größt mögliche Zahl sein)
2. Berechne z = a m mod p.
Ist z = 1 oder z = p – 1, dann hat p den Test für dieses a bestanden.
Sonst wird j:= 0 gesetzt und es beginnt eine Schleife:
3. Falls j > 0 und z = 1, so ist p nicht prim und hat den somit den Test nicht
bestanden.
4. j wird um 1 erhöht:
falls z = p – 1, so hat p für dieses a den Test bestanden.
falls j < b und z ≠ p – 1 , so z:= z 2 mod p und weiter bei 3.
5. Falls j = b und z ≠ p – 1 , so hat p den Test nicht bestanden.
P
P
P
P
P
P
Beim ersten Schritt ist zu p ein m und ein b zu finden, mit p = 2 b m +1. Zudem soll b
auch noch maximal sein.
Das scheint schwieriger, als es in Wirklichkeit ist: Nehmen wir beispielsweise p = 31,
so ist p-1 = 30. Die Zahl 30 wird nun solange durch 2 geteilt, bis „es nicht mehr geht“.
(Einmal muss es immer funktionieren, da p als Primzahl ≠ 2 ungerade sein muss)
P
P
127
HBS Kryptologie
Hier erhält man also 30 = 2 1 * 15 und somit b = 1 und m = 15.
P
P
Hier nun der versprochene Versuch, den Test plausibel zu machen:
Primzahlen sind ganz besondere Zahlen. Für sie gibt es Aussagen, die für andere
Zahlen (Nicht-Primzahlen) nicht gültig sind. Ein Beispiel:
Satz:
Falls p eine Primzahl ist, und für z mit 1 ≤ z < p gilt: z·z = 1 mod p,
dann gibt es genau zwei Zahlen z, die die Gleichung erfüllen:
z = 1 mod p und z = p-1 mod p.
Ein Beispiel:
p = 7 . Nun muss ein z < 7 gefunden werden, sodass z·z = 1 mod 7.
Neben z = 1 erfüllt auch z = 6 die Bedingung, denn 6 2 = 36 und somit 6 2 mod 7 = 1.
Hier ist also offensichtlich z = p-1 = 7-1 = 6. Andere z<7 erfüllen die Gleichung nicht.
P
P
P
P
Was passiert, falls p keine Primzahl ist?
Aufgabe 10:
Testen sie für p = 6 und für p = 8 den obigen Satz.
Was zeigt die Aufgabe?
• Am Beispiel p = 6: Der Satz ist nicht umkehrbar, denn 6 ist keine Primzahl und
dennoch erfüllen genau die angegebenen zwei Zahlen unter 6 die Gleichung
z·z = 1 mod 6, nämlich z=1 und z=5.
• p = 8 kann keine Primzahl sein, da es 4 Zahlen kleiner 8 gibt, die die
Gleichung erfüllen, nämlich 1, 3, 5 und 7.
Beweis des Satzes:
z 2 =1 mod p.
⇒ p│z 2 -1 ⇔ p│(z+1) (z-1)
⇒ p│(z-1)
v p│(z+1)
⇔ z-1 = 0 mod p v z+1 = 0 mod p
⇒ z = 1 mod p
v z = -1 mod p = p-1 mod p
P
P
P
P
(“-1” bedeutet hier diejenige Zahl, die mit 1 addiert 0 modulo p ergibt. Und das ist
ersichtlich die Zahl p-1)
Für Primzahlen p gibt es also genau zwei Zahlen für die z·z = 1 mod p, nämlich die
Zahlen 1 und p-1.
Die Idee ist nun, abhängig von p und von der zufällig gewählten Zahl a, eine
„geschickte“ Zahl z zu errechnen: z = a m mod p.
Weshalb ist dieses z „geschickt“? Man kann zeigen, dass diese Zahl keine Primzahl
ist, falls z bei fortlaufender Quadrierung erstmals den Wert 1 angenommen hat, ohne
P
P
128
HBS Kryptologie
vorher den Wert p-1 angenommen zu haben.
(Aus dem oben angegebenen Algorithmus kann man entnehmen, dass man nicht
endlos quadrieren muss. Es reicht offensichtlich, wenn man die Schleife b mal
durchläuft. Zur Erinnerung: b gibt an, wie oft der Faktor 2 in p-1 steckt.)
Will man den Beweis für den Rabin-Miller-Test vollständig durchführen, so ist zu
zeigen, dass solch „geschickt“ gewählte Zahlen z die angegebene Eigenschaft
tatsächlich haben. Und genau das wird im oben erwähnten Beweis von Florian
Rienhardt durchgeführt.
Natürlich gibt es auch ganz andere Beweise. Hier hilft eine Anfrage bei Google.
Drei Ergänzungen:
1. Unterzieht man eine VLI p einem Primtest, so ist ratsam, zunächst zu prüfen,
ob p „kleine“ Teiler hat. Dazu lassen wir von einem früheren Programm
sämtliche Primzahlen bis 100 000 angeben und teilen p der Reihe nach durch
diese Primzahlen. Dies beschleunigt das Verfahren erheblich!
2. Es ist nicht weiter schlimm, dass der RM-Test sich mit einer Wahrscheinlichkeit von 0,25 irrt. Wenn wir den Test zum Beispiel mit 10 verschiednen aWerten durchführen und der Test in allen Fällen bestanden wird, so ist die
Irrtumswahrscheinlichkeit (0,25 10 ) zehn mal kleiner, als 6 Richtige im Lotto zu
haben!!
3. Die Zahlen a, die man zufällig für den Test aussuchen muss, nehmen wir der
Reihe nach aus der Primzahlliste aus 1.
P
P
Aufgabe 11:
Versuchen Sie den angegebenen Rabin-Miller-Algorithmus in ein Programm in
Delphi (procedure) zu übersetzten.
( z.B.: procedure TForm1.MillerRabinPrim(iw: Double; c: string);
“iw” als Irrtumswahrscheinlichkeit und c als übergebene Zufallszahl mit der
geforderten Stellenzahl, die nun den RM-Test durchlaufen muss.
Lösungsvorschlag auf der nächsten Seite! )
Wenn die Aufgabe 11 gelöst ist, kann man an die Erstellung des Programmes
„Rabin-Miller-Test“ gehen. Hier ein Vorschlag für die Oberfläche:
129
HBS Kryptologie
Einige Ideen, die es Ihnen erleichtern sollen, das Programm wirklich selbst zu
schreiben:
•
Die Zufallszahl c (string) kann man sich so erstellen lassen, dass man, je nach
Anzahl der gewünschten Stellen, Stelle für Stelle mit „random(10)“ bestimmt.
Zum Beispiel so:
For i := 1 to n do
s := s + IntToStr(Random(10));
• Das zweite Editfeld, das die erste Zahl nach der Zufallszahl c angibt, die keine
Trivialteiler (also solche unter 100 000) hat, dient nur der Kontrolle.
Selbstverständlich wird man sie und auch die Zufallszahl dem User
vorenthalten.
• Es ist für die Lesbarkeit des Programms von Vorteil, wenn man die
Rechnungen für VLI (siehe letztes Programm) und auch den Test, ob c
Trivialteiler hat, in je eine eigene Unit packt. Wir verwenden die Namen:
VLIrechnen und kleinePrimteiler.
Die Unit VLIrechnen beinhaltet einfach nur alle Rechnungen für VLI, so wie sie
im letzten Programm festgelegt wurden. Natürlich benötigt man dazu auch die
Unit mTVLInt
Lösung Aufgabe 11 und Quelltext für das gesamte Programm:
unit VLIrechnen;
interface
uses mTVLint, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Menus ;
var v1, v2 : TVLInt;
function ggT_erw(a,b:string; var u,v:string): string;
function ggT(a,b:string): string;
function Plus(Z1,Z2:string):string;
function Min(Z1,Z2:string):string;
130
HBS Kryptologie
function Gdiv(Z1,Z2:string):string;
function Gmod(Z1,Z2:string):string;
function Mult(Z1,Z2:string):string;
function GrGl(Z1,Z2:string):boolean;
function Gr(Z1,Z2:string):boolean;
function Gl(Z1,Z2:string):boolean;
function Godd(z:string):boolean;
function Hoch(Z1:string;e: cardinal):string;
function RSA_invers(e,m:string):string;
function einweg(x,e,m:string):string;
implementation
Die implementation kennen Sie! (Skript ab S. 181)
unit kleinePrimteiler;
interface
uses mTVLInt, Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, math, ExtCtrls, VLIrechnen;
function HasSmallPrimFactor(z: string): Boolean;
const maxPrimIndex = 9591;
primes : Array[0..maxPrimIndex] of Integer =
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
...................................................................................
,99767,99787,99793,99809,99817,99823,99829,99833,99839,99859
,99871,99877,99881,99901,99907,99923,99929,99961,99971,99989
,99991);
implementation
function HasSmallPrimFactor(z: string): Boolean;
var i : Integer; bz, t : string;
begin
Result := False;
bz := z;
t :='1';
i := 0;
While (Not Result) and (i < maxPrimIndex) do
begin
t:=inttostr(primes[i]);
bz:= z;
bz:=Gmod(bz,t);
If Gl(bz,'0') then
Result := True
else
i := i + 1;
end;
end;
end.
131
HBS Kryptologie
Die vollständige Liste der Primzahlen finden sie auf dem Tauschverzeichnis. Sie können die
Liste aber ebenso gut selbst erzeugen, indem Sie z.B. das Programm Primtest (S. 169) so
abändern, dass alle gefundenen Primzahlen auf ein Memo-Feld ausgegeben werden.
unit Unit1; // Hauptunit
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, math, VLIrechnen, mTVLInt, kleinePrimteiler, ExtCtrls;
........................................................................................................................
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var n, i : Integer;
iw : Double;
s : String;
cand,
zwei : TVLInt;
begin
Edit2.Text := '';
Edit4.Text := '';
Edit5.Text := '';
s := '';
n := StrToInt(Edit1.Text);
iw := StrToFloat(Edit3.Text);
For i := 1 to n do
s := s + IntToStr(Random(10));
If Not Godd(s) then
s:=Plus(s,'1');
Edit2.Text := s;
Application.ProcessMessages;
While HasSmallPrimFactor(s) do
s:=Plus(s,'2');
Edit4.Text := s;
Application.ProcessMessages;
MillerRabinPrim(iw, s);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize; //damit nicht immer die gleiche “Zufallszahl” erzeugt wird
end;
procedure TForm1.MillerRabinPrim(iw: Double; c: string);
var tiw : Integer;
function IsMillerRabinPrime(n: string; t: Integer): Boolean;
132
HBS Kryptologie
var r, a, nm1, y : string; k, i, j
: Integer;
begin
Result := True;
a := '0';
nm1 := Min(n,'1');
r := nm1;
k := 0; y:='0';
While Not Godd(r) do
begin
k := k + 1;
r:=GDiv(r,'2');
end; //of while not
For i := 1 to t do
begin
a:= inttostr(Primes[i-1]);
y:= einweg(a,r,n);
If (Not Gl(y,'1')) and (Not Gl(y,nm1)) then
begin
j := 1;
While (j < k) and (Not Gl(y,nm1)) do
begin
y:= einweg(y,'2',n);
If Gl(y,'1') then
begin
Result := False;
Exit;
end;
j := j + 1;
end; //of while and ..
If Not Gl(y,nm1) then
begin
Result := False;
Exit;
end;
end; //of if and not...
end; //of for...
end; //of function
begin
If Not Godd(c) then //falls c gerade dann 1 addieren
c:= Plus(c,'1');
tiw := Ceil(Ln(iw)/Ln(0.25));
Repeat
c:=Plus(c,'2');
While HasSmallPrimFactor(c) do
c:=Plus(c,'2');
until IsMillerRabinPrime(c, tiw);
edit5.Text:= c;
end;
end.
Aufgabe 12:
133
HBS Kryptologie
Testen Sie das Programm!
Weshalb dauert es nicht immer gleich lang, bis eine Primzahl einer bestimmten
Länge gefunden wird?
Versuchen Sie eine 150-stellige Primzahl zu finden. Wie lange hat der Rechner dazu
gebraucht?
(„Primzahl“ ist im Sinne von „sehr, sehr, sehr wahrscheinlich Primzahl“ )
Ergänzung zum Screenshot S. 189:
Für die 80-stellige Primzahl benötigte ein (im Jahr 2006) sehr schneller Computer
(AMD X2 4800+, 2GB RAM) immerhin noch knapp eine Minute.
Die „Zufallszahl“ endet mit 21, der erste ernsthafte Kanditat ist nur um 40 größer, als
die Zufallszahl. Dazu musst der Rechner 20 „triviale“ Tests durchführen.
Und schon die nächste zu testende Zahl ist nach Rabin-Miller prim. So viel Glück hat
man nicht immer.......
Sicheres Verschlüsseln und Entschlüsseln mit RSA
Hatten wir diese Überschrift nicht schon auf S.163? Nicht ganz. Dort fehlte (zurecht)
der Begriff „sicher“. Es geht also im Folgenden nur darum, das Programm „RSAKomplett“ für VLI umzuschreiben.
Der Programm-Ordner „RSA_komplett_VLI“ sollte die Units VLIrechnen, mTVLInt
und kleinePrimteiler beinhalten.
Soweit nicht schon geschehen, müssen die Units für Ver- und Entschlüsseln noch
umgeschrieben werden. Die Unit für die Schlüsselberechnung haben wir ja schon.
Um etwas Ordnung in das Programm zu bekommen, starten wir mit einem kleinen
Formular und der zugehörigen Unit 1:
Von hier aus kann man zu
jedem Teil des Programms
starten. Die Image-Komponente
wurde mit einem Picture
versorgt, so dass das
Startfenster etwas freundlicher
aussieht. Suchen Sie sich
selbst ein geeignetes Bild im
Internet und legen Sie es in den
Programm-Ordner.
Ansonsten ist Unit 1 recht
banal:
unit Unit1; //Start-Unit
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, Buttons, jpeg;
type
134
HBS Kryptologie
TFStart = class(TForm)
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
BitBtn3: TBitBtn;
Image1: TImage;
procedure BitBtn1Click(Sender: TObject);
procedure BitBtn3Click(Sender: TObject);
procedure BitBtn2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FStart: TFStart;
implementation
uses Unit2, Unit3,Unit4;
{$R *.dfm}
procedure TFStart.BitBtn1Click(Sender: TObject);
begin
FSchluesselbestimmen.Show;
end;
procedure TFStart.BitBtn3Click(Sender: TObject);
begin
Fverschluesseln.Show;
end;
procedure TFStart.BitBtn2Click(Sender: TObject);
begin
Fentschluesseln.show;
end;
end.
Wie die Formulare für Schlüsselbestimmung, Verschlüsseln und Entschlüsseln
heißen werden, können Sie dem Quelltext oben entnehmen.
Unit2 für die Schlüsselbestimmung und das zugehörige Formular wurden, den
Anforderungen ensprechend, leicht gegenüber der letzten Fassung verändert:
135
HBS Kryptologie
Man soll von hier aus auch direkt auf die entsprechenden Formulare zu Ver-und
Entschlüsseln kommen.
Ebenso muss es eine Möglichkeit geben, die erhaltenen Schlüssel zu speichern.
(Memo1):
Im Folgenden Quelltext sind nur die Änderungen angegeben:
unit Unit2; //Schlüssel bestimmen
............................................................................................
procedure TFSchluesselBestimmen.Nachrichtverschlsseln1Click(Sender: TObject);
begin
Fverschluesseln.show;
Fverschluesseln.edit1.Text:= edit4.Text;
Fentschluesseln.edit1.Text:= edit5.text;
Fentschluesseln.edit3.Text:= edit2.text;
Fentschluesseln.edit4.Text:= edit4.text;
end;
procedure TFSchluesselBestimmen.Schlsselbestimmen1Click(Sender: TObject);
var n, i : Integer; iw : Double; s : String;
cand, zwei : TVLInt;
begin
w:= 1; n_oeffentlich:='1';
while w <= 2 do //zwei Primfaktoren suchen
begin
w:= w+1;
136
HBS Kryptologie
s := '';
Label3.Caption:= '
BITTE WARTEN!!! ' ;
Label7.Caption:='Die Suche kann mehrere Minuten dauern! ' ;
n := StrToInt(Edit1.Text);
iw := StrToFloat(Edit3.Text);
For i := 1 to n do
s := s + IntToStr(Random(10));
If Not Godd(s) then
s:=Plus(s,'1');
Application.ProcessMessages;
While HasSmallPrimFactor(s) do
s:=Plus(s,'2');
Application.ProcessMessages;
MillerRabinPrim(iw, s);
end;
Label3.Caption:=''; Label7.Caption:='';
end;
procedure TFSchluesselBestimmen.ffentlichenSchlsselspeichern1Click(
Sender: TObject);
var i,j:word;
begin
j:=0;
while j<4 do //4 mal löschen wegen überlanger Zeilen
begin
for i:=10 downto 0 do
begin
Memo1.Lines[I]:='';
end;
j:=j+1;
end;
Memo1.Lines[0]:='
Öffentlicher Schlüssel';
Memo1.Lines[2]:='n = '+edit4.Text;
Memo1.Lines[4]:='e = 17' ;
if SaveDialog1.Execute
then begin
Try
Memo1.Lines.SavetoFile(SaveDialog1.filename);
Except
Application.MessageBox ('Datei konnte nicht gespeichert werden!',
'Fehler', MB_OK);
end; {Try}
end;
end;
procedure TFSchluesselBestimmen.privatenSchlsselspeichern1Click(
Sender: TObject);
var j,i:word;
begin
j:=0;
while j<4 do //4 mal löschen wegen überlanger Zeilen
begin
for i:=10 downto 0 do
137
HBS Kryptologie
begin
Memo1.Lines[I]:='';
end;
j:=j+1;
end;
Memo1.Lines[0]:='
Privater Schlüssel';
Memo1.Lines[2]:='p = '+edit5.Text;
Memo1.Lines[4]:='q = '+edit2.Text;
Memo1.Lines[6]:='e = 17' ;
Memo1.Lines[8]:='n = '+edit4.Text;
if SaveDialog1.Execute
then begin
Try
Memo1.Lines.SavetoFile(SaveDialog1.filename);
Except
Application.MessageBox ('Datei konnte nicht gespeichert werden!',
'Fehler', MB_OK);
end; {Try}
end;
end;
procedure TFSchluesselBestimmen.Nachrichtentschlsseln1Click(
Sender: TObject);
begin
Fentschluesseln.show;
Fentschluesseln.edit4.Text:= edit4.Text;
Fentschluesseln.edit1.Text:= edit5.Text;
Fentschluesseln.edit3.Text:= edit2.Text;
end;
end.
Nun zum Verschlüsseln:
Die Unit3 für das Verschlüsseln entsteht durch Umschreibung der Rechenoperationen für
VLInt.
Zwei Änderungen gibt es aber doch:
•
Beim Verschlüsseln werden jetzt immer sieben Zeichen, statt wie früher 2 Zeichen,
zusammengefasst. Der Wert 7 ist ein Kompromiss. Nehmen Sie an, dass der
öffentliche Schlüssel n 200 Dezimalstellen habe. Dann würden zwei Zeichen in eine
200-stellige Zahl verwandelt. Kein sehr sinnvolles Verhältnis.
Nimmt man hingegen zu viele Zeichen zusammen, so wird das Ergebnis zu groß für
Int64 und man müsste auch diesen Teil mit VLInt durchführen.
•
Im Menu Datei findet man die auf den ersten Blick seltsame Anweisung „Zeilen
umbrechen“. Schaut man sich den Quelltext weiter unten an, so erkennt man, dass
eine Zeile nicht länger als 35 Zeichen lang sein soll.
Dazu muss man wissen, dass die Memo-Komponenten in Delphi eine maximale
Zeilenlänge von 1024 Zeichen haben. Diesen Wert kann man auch nicht mit dem
Objektinspektor ändern.
Um aber den Schlüssel über 512 Bit zu bekommen, muss n mindestens 150
Dezimalstellen haben. Unser Programm fasst aber immer 7 Zeichen des
unverschlüsselten Textes zusammen. Nehmen wir an, dass n noch etwas sicherer ist
und 200 Stellen hat, dann würden 35 Zeichen gerade eben in eine Zeile passen.
138
HBS Kryptologie
(35 : 7 = 5; 5*200 = 1000 passt!)
Bleibt die Frage offen, warum denn nicht einfach in der nächste Zeile weiter
geschrieben werden soll.
Das liegt an der Konzeption unseres Programms, dass den Text sowohl zum
Verschlüsseln als auch zum Entschlüsseln immer zeilenweise ausliest. Wird der
verschlüsselte Text länger als 1024, so ist die Zahl für die letzten 7 Ziffern auf zwei
Zeilen verteilt. Das kann nicht gut gehen!
Und hier der zugehörige Quelltext (sofern er sich verändert hat):
unit Unit3; //Verschlüsseln
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, mTVLInt,VLIrechnen, Menus;
…………………………………………………………………………………….
private
{ Private declarations }
p,q,n,m,e,d,x : string;
v1, v2 : TVLInt;
function InASCIIundVerschl(Zeile: string):string;
public
{ Public declarations }
end;
139
HBS Kryptologie
var
FVerschluesseln: TFVerschluesseln;
implementation
uses Unit4;
{$R *.dfm}
function TFVerschluesseln.InASCIIundVerschl(Zeile:string):string;
var i,L,LM: word; Zahl1,Zahl2,Zahl3,Zahl4,Zahl5,Zahl6,Zahl7,FB:int64;
HS,Mo:string;
begin
result:=''; HS:=''; x:='';
L:=length(Zeile);
Mo:=n; LM:=Length(Mo);
i:=1;
while i<=L do // es werden immer 7 Zeichen zusammengefasst
begin
Zahl1:=ord(Zeile[i]); Zahl2:=ord(Zeile[i+1]);
Zahl3:=ord(Zeile[i+2]); Zahl4:=ord(Zeile[i+3]);
Zahl5:=ord(Zeile[i+4]); Zahl6:=ord(Zeile[i+5]);
Zahl7:=ord(Zeile[i+6]) ;
FB:=(((((256*Zahl7+Zahl6)*256+Zahl5)*256+Zahl4)*256+Zahl3)*256+Zahl2)*256+Zahl1;
x:=inttostr(FB);
HS:= einweg(x,e,n);
while length(HS)<LM do
begin
HS:='0'+HS;
end;
result:=result+ HS;
i:=i+7;
end;
L:=length(result);
for i:=1 to L do
begin
if result[i]=' ' then result[i]:='0';
end;
end;
procedure TFVerschluesseln.verschluesseln1Click(Sender: TObject);
var I,J : Integer; Zeile: string;
begin
e:=edit2.Text;
n:=edit1.Text;
j:= Memo1.Lines.Count;
for i:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
Memo2.Lines[I]:=InASCIIundVerschl(Zeile);
end;
FEntschluesseln.Memo1.Text:= Memo2.Text;
end;
140
HBS Kryptologie
procedure TFVerschluesseln.extspeichern1Click(Sender: TObject);
begin
if SaveDialog1.Execute
then begin
Try
Memo2.Lines.SavetoFile(SaveDialog1.filename);
Except
Application.MessageBox ('Datei konnte nicht gespeichert werden!',
'Fehler', MB_OK);
end; {Try}
end;
end;
procedure TFVerschluesseln.extladen1Click(Sender: TObject);
begin
OpenDialog1.Options := [ofFileMustExist];
if OpenDialog1.Execute
then begin
Try
Memo1.Lines.LoadfromFile(OpenDialog1.FileName);
Except
Application.MessageBox ('Datei konnte nicht geladen werden!',
'Fehler', MB_OK);
end; {Try}
end;
end;
procedure TFVerschluesseln.ErstesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do //10 mal löschen wegen überlanger Zeilen
begin
for i:=500 downto 0 do
begin
Memo1.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TFVerschluesseln.ZweitesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=500 downto 0 do
begin
Memo2.Lines[I]:='';
end;
j:=j+1;
141
HBS Kryptologie
end;
end;
procedure TFVerschluesseln.DrittesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
memo3.Text:='';
end;
procedure TFVerschluesseln.UnteresFeldhochkopieren1Click(Sender: TObject);
begin
Memo1.Text:= Memo2.Text;
end;
procedure TFVerschluesseln.Beenden1Click(Sender: TObject);
begin
close;
end;
procedure TFVerschluesseln.Zeilenumbrechen1Click(Sender: TObject);
var I,J,k,n: Integer; Zeile,Zeile1,Leerzeichen: string;
gefunden:boolean;
begin
j:= Memo1.Lines.Count; Leerzeichen:=' ';
for i:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
k:= 15;
gefunden:=false;
n:=Length(Zeile);
if n<=35 then Memo3.Lines.Add(Zeile) else
begin
repeat
while gefunden=false do //es wird davon ausgegangen,
//dass sich zwischen 15 und 35 mindestens
//eine Leerstelle befindet
begin
if Zeile[k]=Leerzeichen then
begin
Zeile1:=Copy(Zeile,1,k-1) ;
Zeile:= Copy(Zeile,k,n) ;
Memo3.Lines.Add(Zeile1) ;
gefunden:=true;
end
else
k:=k+1;
end; //von while
n:=Length(Zeile); k:= 15;
if n>35 then gefunden:=false;
until n<=35;
Memo3.Lines.Add(Zeile);
142
HBS Kryptologie
end; //else
end;
ErstesFeldlschen1Click(Sender);
Memo1.Text:=Memo3.Text;
DrittesFeldlschen1Click(Sender);
Memo3.Text:='';
end;
end.
Die Unit4 für das Entschlüsseln birgt keine Überraschungen. Hier wurden tatsächlich
ausschließlich die Rechnungen für VLInt und beim zugehörigen Fenster die Größe der
Editfelder angepasst:
unit Unit4; //Entschlüsseln
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, mTVLInt,VLIrechnen,Menus;
private
{ Private declarations }
p,q,n,m,e,d,x : string;
function EntschlUndInText(Zeile: string):string;
…………………………………………………………………………………
143
HBS Kryptologie
public
{ Public declarations }
end;
var
FEntschluesseln: TFEntschluesseln;
implementation
{$R *.dfm}
function TFEntschluesseln.EntschlUndInText(Zeile:string):string;
var L,LM: word;
HS,Mo,SZahl1,SZahl2,SZahl3,SZahl4,SZahl5,SZahl6,SZahl7:string;
C1,C2,C3,C4,C5,C6,C7: char; I,K:integer;
begin
L:=Length(Zeile);
Mo:=n ;LM:=Length(Mo);
result:=''; HS:=''; I:=-LM+1;
While I<=L-LM do
begin
I:=I+LM;
HS:=Copy(Zeile,I,LM); //LM-lange Teilstrings werden hier von Zeile auf HS kopiert
HS:=einweg(HS,d,n);
SZahl1:=Gmod(HS,'256'); HS:=Gdiv(HS,'256');
SZahl2:=Gmod(HS,'256'); HS:=Gdiv(HS,'256');
SZahl3:=Gmod(HS,'256'); HS:=Gdiv(HS,'256');
SZahl4:=Gmod(HS,'256'); HS:=Gdiv(HS,'256');
SZahl5:=Gmod(HS,'256'); HS:=Gdiv(HS,'256');
SZahl6:=Gmod(HS,'256'); SZahl7:=Gdiv(HS,'256');
C1:=chr(strtoint(SZahl1));C2:=chr(strtoint(SZahl2));C3:=chr(strtoint(SZahl3));
C4:=chr(strtoint(SZahl4));C5:=chr(strtoint(SZahl5));
C6:=chr(strtoint(SZahl6));C7:=chr(strtoint(SZahl7));
result:=result+C1+C2+C3+C4+C5+C6+C7;
end; //for L
end;
procedure TFEntschluesseln.extspeichern1Click(Sender: TObject);
begin
if SaveDialog1.Execute
then begin
Try
Memo2.Lines.SavetoFile(SaveDialog1.filename);
Except
Application.MessageBox ('Datei konnte nicht gespeichert werden!',
'Fehler', MB_OK);
end; {Try}
end;
end;
procedure TFEntschluesseln.extladen1Click(Sender: TObject);
begin
144
HBS Kryptologie
OpenDialog1.Options := [ofFileMustExist];
if OpenDialog1.Execute
then begin
Try
Memo1.Lines.LoadfromFile(OpenDialog1.FileName);
Except
Application.MessageBox ('Datei konnte nicht geladen werden!',
'Fehler', MB_OK);
end; {Try}
end;
end;
procedure TFEntschluesseln.ErstesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=0 to 500 do
begin
Memo1.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TFEntschluesseln.ZweitesFeldlschen1Click(Sender: TObject);
var i,j:word;
begin
j:=0;
while j<10 do
begin
for i:=0 to 500 do
begin
Memo2.Lines[I]:='';
end;
j:=j+1;
end;
end;
procedure TFEntschluesseln.UnteresFeldhochkopieren1Click(Sender: TObject);
begin
Memo1.Text:= Memo2.Text;
end;
procedure TFEntschluesseln.dfrEntschlsselungberechnen1Click(Sender: TObject);
begin
e:=edit2.Text; n:=edit4.Text;
p:=edit1.Text; q:=edit3.text; m:=Mult(Min(p,'1'),Min(q,'1'));
d:= RSA_invers(e,m);
label4.Caption:='d= '; edit3.text:=d;
end;
145
HBS Kryptologie
procedure TFEntschluesseln.entschlsseln1Click(Sender: TObject);
var I,J : Integer; Zeile: string;
begin
J:= Memo1.Lines.Count;
for I:=0 to J-1 do
begin
Zeile:=Memo1.Lines[I];
Memo2.Lines[I]:=EntschlUndInText(Zeile);
end;
end;
………………………………………………………………
Wenn Sie das Programm testen, wählen Sie keine zu langen Texte für die Verschlüsselung.
Sie würden es bei der Entschlüsselung bereuen............
Damit nach der ganzen Arbeit keine Frustration wegen der ewig langen Rechenzeiten
aufkommt („ist das Verfahren überhaupt sinnvoll?“ etc.) wird dringend geraten, die
Überschrift zu diesem Kapitel nochmals zu lesen: Sichere Schlüsselübergabe mir RSA!!!
Also keine Sorge, RSA taugt durchaus. Im nächsten Kapitel werden Sie erfahren, wie man
sinnvoll mit RSA arbeitet.
146
Herunterladen