3. Kryptologie
Bei den hier besprochenen Verfahren geht es nicht um die Kryptoanalyse, d.h. die Beurteilung der Sicherheit der Verfahren, sondern um die Umsetzung der Verfahren in Java. Dabei kommen elementare Inhalte wie Zeichenketten, Felder und vor allem das Verwandeln von char-Buchstaben in int-Zahlen und umgekehrt nach der ASCII-Tabelle zum Tragen.
Caesar-Verschlüsselung
Das wohl bekannteste Verschlüsselungsverfahren geht auf Julius Caesar zurück. Er verschob die Buchstaben des Alphabets einfach um eine feste Anzahl von Buchstaben. Jedem Klartextbuchstaben wird damit ein verschobener Geheimtextbuchstabe zugeordnet. Bei der Entschlüsselung geschieht die Verschiebung dann in entgegengesetzter Richtung.
Einfache Umsetzung
Die folgende Umsetzung greift den Gedanken auf und ordnet im Rahmen einer switch-Anweisung die Buchstaben einander zu. Bei der zugehörigen Entschlüsselungsmethode würde die Anordnung entgegengesetzt erfolgen.
public String verschluesseleEinfach(String klartext) {
String ergebnis = "";
for (int i=0; i<klartext.length(); i++) {
switch (klartext.charAt(i)) {
case 'A': { ergebnis = ergebnis + 'D'; break; }
case 'B': { ergebnis = ergebnis + 'E'; break; }
case 'C': { ergebnis = ergebnis + 'F'; break; }
case 'D': { ergebnis = ergebnis + 'G'; break; }
case 'E': { ergebnis = ergebnis + 'H'; break; }
case 'F': { ergebnis = ergebnis + 'I'; break; }
case 'G': { ergebnis = ergebnis + 'J'; break; }
case 'H': { ergebnis = ergebnis + 'K'; break; }
case 'I': { ergebnis = ergebnis + 'L'; break; }
case 'J': { ergebnis = ergebnis + 'M'; break; }
case 'K': { ergebnis = ergebnis + 'N'; break; }
case 'L': { ergebnis = ergebnis + 'O'; break; }
case 'M': { ergebnis = ergebnis + 'P'; break; }
case 'N': { ergebnis = ergebnis + 'Q'; break; }
case 'O': { ergebnis = ergebnis + 'R'; break; }
case 'P': { ergebnis = ergebnis + 'S'; break; }
case 'Q': { ergebnis = ergebnis + 'T'; break; }
case 'R': { ergebnis = ergebnis + 'U'; break; }
case 'S': { ergebnis = ergebnis + 'V'; break; }
case 'T': { ergebnis = ergebnis + 'W'; break; }
case 'U': { ergebnis = ergebnis + 'X'; break; }
case 'V': { ergebnis = ergebnis + 'Y'; break; }
case 'W': { ergebnis = ergebnis + 'Z'; break; }
case 'X': { ergebnis = ergebnis + 'A'; break; }
case 'Y': { ergebnis = ergebnis + 'B'; break; }
case 'Z': { ergebnis = ergebnis + 'C'; break; }
default: { ergebnis = ergebnis + klartext.charAt(i); }
}
}
}
Geschickte Umsetzung
Ein geschickterer Ansatz macht sich die ASCII-Tabelle zunutze. Er durchläuft den Klartext Buchstabe für Buchstabe. Für jeden Buchstaben wird die Nummer in der ASCII-Tabelle ermittelt und dieser Nummer der Schlüssel (d.h. die Anzahl der zu verschiebenden Stellen) hinzuaddiert. Anschließend wird die so ermittelte Nummer wieder in einen Buchstaben zurückverwandelt (Bildausschnitt aus WikiPedia).
Dabei kann es passieren, dass das Alphabet verlassen wird (Z entspricht 90 am Ende des Alphabets). In diesem Fall geht es durch Subtraktion von 26 wieder im Alphabet von vorn los.
Elegant ist die Idee, die Entschlüsselung durch einen Schlüssel als negative Zahl zu realisieren. Dazu muss zusätzlich mit überprüft werden, ob das Alphabet nach vorn (A entspricht 65 am Anfang des Alphabets) verlassen wird.
public String verschluessele(String zeichenkette, int schluessel) {
String ergebnis = "";
for (int i=0; i<zeichenkette.length(); i++) {
int zahl = (int) zeichenkette.charAt(i);
zahl = zahl + schluessel;
if (zahl>90) { zahl = zahl - 26; }
if (zahl<65) { zahl = zahl + 26; }
char zeichen = (char) zahl;
ergebnis = ergebnis + zeichen;
}
return ergebnis;
}
public String entschluessele(String zeichenkette, int schluessel) {
return verschluessele(zeichenkette, -schluessel);
}
Monoalphabetische Substitution
Da die Verschiebung des Alphabets durch die Caesar-Verschlüsselung zu wenig Sicherheit bietet, ordnet die monoalphabetische Substitution jedem Buchstaben des Alphabets zufällig einen anderen Buchstaben des Alphabets zu. Es ergeben sich 26! mögliche Schlüssel.
Einfache Umsetzung
Auch hier ist zunächst wieder eine Zuordnung der Buchstaben in einer switch-Anweisung denkbar:
public String verschluessele(String klartext) {
String ergebnis = "";
for (int i=0; i<klartext.length(); i++) {
switch (klartext.charAt(i)) {
case 'A': { ergebnis = ergebnis + 'Q'; break; }
case 'B': { ergebnis = ergebnis + 'W'; break; }
case 'C': { ergebnis = ergebnis + 'E'; break; }
case 'D': { ergebnis = ergebnis + 'R'; break; }
case 'E': { ergebnis = ergebnis + 'T'; break; }
case 'F': { ergebnis = ergebnis + 'Z'; break; }
case 'G': { ergebnis = ergebnis + 'U'; break; }
case 'H': { ergebnis = ergebnis + 'I'; break; }
case 'I': { ergebnis = ergebnis + 'O'; break; }
case 'J': { ergebnis = ergebnis + 'P'; break; }
case 'K': { ergebnis = ergebnis + 'A'; break; }
case 'L': { ergebnis = ergebnis + 'S'; break; }
case 'M': { ergebnis = ergebnis + 'D'; break; }
case 'N': { ergebnis = ergebnis + 'F'; break; }
case 'O': { ergebnis = ergebnis + 'G'; break; }
case 'P': { ergebnis = ergebnis + 'H'; break; }
case 'Q': { ergebnis = ergebnis + 'J'; break; }
case 'R': { ergebnis = ergebnis + 'K'; break; }
case 'S': { ergebnis = ergebnis + 'L'; break; }
case 'T': { ergebnis = ergebnis + 'Y'; break; }
case 'U': { ergebnis = ergebnis + 'X'; break; }
case 'V': { ergebnis = ergebnis + 'C'; break; }
case 'W': { ergebnis = ergebnis + 'V'; break; }
case 'X': { ergebnis = ergebnis + 'B'; break; }
case 'Y': { ergebnis = ergebnis + 'N'; break; }
case 'Z': { ergebnis = ergebnis + 'M'; break; }
default: { ergebnis = ergebnis + klartext.charAt(i); }
}
}
return ergebnis;
}
Geschickte Umsetzung (Schlüssel als Zeichenkette)
Bei der ersten geschickten Alternative wird der Schlüssel als Zeichenkette mit 26 Buchstaben repräsentiert (z.B. analog zur Umsetzung oben: "QWERTZUIOPASDFGHJKLYXCVBNM"
). Bei der Umwandlung eines Klartextbuchstaben wird dieser in eine fortlaufende Nummer umgewandelt (also z.B. A in 0). Diese Nummer gibt an, an welcher Stelle der Schlüssel-Zeichenkette der zugeordnete Geheimtextbuchstabe zu finden ist.
public String verschluesseleSchlau(String zeichenkette, String schluessel) {
String ergebnis = "";
for (int i=0; i<zeichenkette.length(); i++) {
int zahl = ((int) zeichenkette.charAt(i)) - 64 - 1;
ergebnis = ergebnis + schluessel.charAt(zahl);
}
return ergebnis;
}
Geschickte Umsetzung (Schlüssel als Feld)
Die zweite Alternative ähnelt der ersten. Hier wird der Schlüssel als Feld über 26 Buchstaben statt als Zeichenkette repräsentiert. Bei der Umwandlung eines Klartextbuchstaben wird dieser wieder in eine fortlaufende Nummer umgewandelt (also z.B. A in 0). Diese Nummer gibt hier an, an welcher Stelle des Feldes der zugeordnete Geheimtextbuchstabe zu finden ist.
public String verschluesseleSchlau(String zeichenkette, char[] schluessel) {
String ergebnis = "";
for (int i=0; i<zeichenkette.length(); i++) {
int zahl = ((int) zeichenkette.charAt(i)) - 65;
ergebnis = ergebnis + schluessel[zahl];
}
return ergebnis;
}
Polyalphabetische Substitution
Idee
Reicht ein Zielalphabet nicht aus, um für genügend Sicherheit zu sorgen, dann nehme man mehrere Zielalphabete, die sich untereinander abwechseln. Diese Idee ist als polyalphabetische Substitution bekannt und wird dem Kryptologen Blaise de Vigenère zugeordnet.
Grundlage des Verfahren ist das Vigenère-Quadrat, dessen Anwendung hier demonstriert wird (Quelle: Wikipedia):
Der Schlüssel (im Beispiel AKEY) gibt an, welches Alphabet gerade aktiv ist, d.h. welche Zeile zu wählen ist. So wird für den ersten Buchstaben des Klartextes die Zeile A gewählt, für den zweiten Buchstaben K, für den dritten Buchstaben E und für den vierten Buchstaben Y. Anschließend geht es wieder von vorne los, also wieder mit dem A.
Um nun z.B. den sechsten Buchstaben des Klartextes M mit dem zugehörigen Schlüsselbuchstaben K zu verknüpfen, wird die Spalte M mit der Zeile K verbunden und der Kreuzungspunkt W als Geheimtextbuchstabe festgehalten. Ein Vertauschen von Zeile und Spalte ist kein Problem, da das Vigenère-Quadrat symmetrisch ist.
Umsetzung
Der erste Teil der Java-Umsetzung besteht aus der Definition des Vigenère-Quadrats.
private char[][] tabelle = {
{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'},
{'B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A'},
{'C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B'},
{'D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C'},
{'E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D'},
{'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E'},
{'G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F'},
{'H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G'},
{'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H'},
{'J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I'},
{'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J'},
{'L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K'},
{'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L'},
{'N','O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M'},
{'O','P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N'},
{'P','Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O'},
{'Q','R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P'},
{'R','S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q'},
{'S','T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R'},
{'T','U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S'},
{'U','V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T'},
{'V','W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U'},
{'W','X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V'},
{'X','Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W'},
{'Y','Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X'},
{'Z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y'}
};
Der zweite Teil setzt die Verknüpfung von Zeile und Spalte um: In der Variablen schluessel_pos wird die Position des aktuellen Schlüsselbuchstaben festgehalten. Dieser Positionszeiger bewegt sich fortlaufend durch die Schlüssel-Zeichenkette und fängt bei Erreichen des Endes wieder vorne an. Die Variable schluessel_nr enthält entsprechend die Nummer des aktuellen Schlüsselbuchstaben. Schließlich legt die Variable klar_nr die Nummer des aktuellen Klartextbuchstaben fest. Durch diese beiden Variablen sind Zeile und Spalte festgelegt, wodurch der Geheimtextbuchstabe in der Tabelle nachgeschaut werden kann.
public String verschluessele(String klartext, String schluesselwort) {
String ergebnis = "";
int schluessel_pos = 0;
for (int i=0; i<klartext.length(); i++) {
char klar = klartext.charAt(i);
int klar_nr = (int) klar - 65;
char schluessel = schluesselwort.charAt(schluessel_pos);
int schluessel_nr = (int) schluessel - 65;
char geheim = tabelle[klar_nr][schluessel_nr];
ergebnis = ergebnis + geheim;
schluessel_pos++;
if (schluessel_pos >= schluesselwort.length()) {
schluessel_pos = 0;
}
}
return ergebnis;
}