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