Fortgeschrittene Algorithmen und Programmierung
Prof. Dr. Alexandra Mikityuk
HTW Berlin
Wir starten ganz einfach (von Hand rechenbar) und arbeiten uns Stück für Stück bis zu dem vor, was euer Browser gerade jetzt benutzt. Jeder Schritt repariert ein Problem des vorigen.
Caesar und XOR rechnen wir mit Bleistift mit — so fühlt man, was Verschlüsseln eigentlich ist.
Jede Idee schreiben wir kurz in C — und sehen, wo die einfachen Versionen scheitern.
AES, RSA und HTTPS — das, was Banken, WhatsApp und euer Browser wirklich nutzen.
Hashing war eine Einbahnstraße: Passwort rein → Hash raus → nie wieder zurück. Genau das wollten wir.
Deine Nachricht reist über viele fremde Stationen: WLAN-Router, Provider, Server. Jede kann mitlesen.
Jeder Briefträger, jede Poststelle kann lesen: „Treffen um 18 Uhr, PIN ist 4321." Im Netz heißt das: jeder Hotspot-Betreiber im Café liest mit.
Unterwegs sieht jeder nur Zeichensalat. Öffnen kann den Brief nur, wer den passenden Schlüssel hat — der Empfänger.
Hashing und Verschlüsselung werden ständig verwechselt. Merkt euch diesen einen Unterschied:
Einbahnstraße. Kein Schlüssel. Man kommt nie zurück.
Zweck: prüfen, ob etwas gleich ist (Passwörter), ohne es zu speichern.
Hin- und Rückweg. Mit Schlüssel kommt man wieder zurück.
Zweck: etwas geheim transportieren und beim Empfänger wieder lesbar machen.
Verschlüsselung ist ein Safe: du legst den Klartext rein, schließt mit dem Schlüssel ab — und nur mit demselben (oder einem passenden) Schlüssel kommst du wieder ran.
| Deutsch | Englisch | Was ist es? |
|---|---|---|
| Klartext | Plaintext | Die lesbare Original-Nachricht |
| Geheimtext | Ciphertext | Der verschlüsselte Zeichensalat |
| Schlüssel | Key | Das Geheimnis, mit dem ab- und aufgeschlossen wird |
| Algorithmus | Cipher | Das Rezept, wie ver-/entschlüsselt wird (z. B. Caesar, AES) |
Schon Julius Caesar hat sie benutzt: jeden Buchstaben um eine feste Zahl im Alphabet weiterschieben. Der Schlüssel ist diese Zahl.
Klartext: A B C D E ... H ... L ... O ... X Y Z
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ (+3)
Geheimtext: D E F G H ... K ... O ... R ... A B C (X Y Z wickeln um!)
"HALLO" → H+3=K A+3=D L+3=O L+3=O O+3=R → "KDOOR"
"KDOOR" → K−3=H, D−3=A, … → "HALLO". Derselbe Schlüssel (3), andere Richtung.
„Einen Buchstaben um 3 weiterschieben" — klingt nach Alphabet-Tabelle. Aber erinnert ihr euch an den Trick aus VL 8?
In C ist char in Wahrheit eine kleine Zahl (ASCII). 'A' = 65, 'B' = 66 …
Wenn 'A' die Zahl 65 ist — was passiert dann wohl bei 'A' + 3?
Und was tun wir bei 'Z' + 3? Da gibt es kein „Ä, Å, Æ" — wir müssen wieder bei 'A' anfangen. Wie?
%).
%) ist ein ZifferblattBevor wir Caesar in C schreiben: % gibt den Rest beim Teilen. Am einfachsten merkt man sich das als Uhr — die zählt auch immer im Kreis.
% 12Nach der 11 kommt wieder 0 — die Uhr „springt zurück". Genau das macht % 12.
0 % 12 = 0 // Mitternacht
5 % 12 = 5
11 % 12 = 11
12 % 12 = 0 // zurück auf 0!
13 % 12 = 1 // 13 Uhr = 1 Uhr
25 % 12 = 1 // 1 Tag + 1 Stunde
Egal wie groß die Zahl wird — das Ergebnis bleibt im Kreis 0…11. Bei % 12 sind das die 12 Stunden, bei % 26 die 26 Buchstaben.
% 26. Z. B. X(23)+3 = 26 → 26 % 26 = 0 → A.
Bei positiven Zahlen merkt man keinen Unterschied. Bei negativen schon — und dann versteht man erst, was Modulo wirklich ist.
-3 % 26 ist in C gleich -3, in Python aber 23. Wer hat recht? Beide — sie wählen nur eine andere Antwort aus derselben Familie.
Jeder Schritt ist +26 (einmal ganz um die Uhr). Alle diese Zahlen landen auf demselben Punkt — sie sind „gleich modulo 26": −3 ≡ 23 (mod 26), weil 23 − (−3) = 26.
% machtC löst diese Gleichung auf (Division wird Richtung 0 abgeschnitten):
a == (a / b) * b + (a % b)
-3 / 26 = 0 // zu 0 hin gerundet
-3 % 26 = -3 // damit Gleichung stimmt
Das Vorzeichen folgt dem Dividenden (links). Negativ rein → negativ raus.
Modulo teilt alle ganzen Zahlen in 26 Familien (Klassen) ein:
[0] = …,-52,-26, 0, 26, 52,…
[23] = …,-29, -3,23, 49, 75,…
Modulo fragt nicht „welcher Rest?", sondern „welche Familie?". Es wählt dann den einen Vertreter 0…25 — deshalb 23.
+ 26 (oder gleich vorwärts mit Schlüssel 23).
Wir rechnen mit den ASCII-Werten. Der Trick: relativ zu 'A' rechnen, um 3 schieben, mit % 26 umwickeln.
#include <stdio.h>
#include <string.h>
void caesar(char text[], int schluessel) {
for (int i = 0; text[i] != '\0'; i++) {
char c = text[i];
// nur Großbuchstaben A-Z verschieben
if (c >= 'A' && c <= 'Z') {
int pos = c - 'A'; // Buchstabe → 0..25 (A=0, B=1, …)
pos = (pos + schluessel) % 26; // schieben und bei Z umwickeln
text[i] = pos + 'A'; // 0..25 → ASCII-Buchstabe zurück
}
}
}
int main(void) {
char nachricht[] = "HALLO";
caesar(nachricht, 3); printf("%s\n", nachricht); // → KDOOR
caesar(nachricht, 23); printf("%s\n", nachricht); // → HALLO (23 = -3 zurück)
return 0;
}
'Z', Schlüssel 3): 'Z'-'A' = 25 → 25+3 = 28 → 28 % 26 = 2 → 2 + 'A' = 'C'. Z wird also zu C — sauber umgewickelt, kein „Ä".
Nehmt euch 30 Sekunden. Wir verschlüsseln und entschlüsseln — und sehen, dass wir genau dort wieder rauskommen, wo wir gestartet sind.
K (+4) → O
A (+4) → E
T (+4) → X
Z (+4) → D // 25+4=29, %26=3 → D
E (+4) → I
"KATZE" → "OEXDI"
O (−4) → K
E (−4) → A
X (−4) → T
D (−4) → Z // wickelt zurück
I (−4) → E
"OEXDI" → "KATZE" ✓
Caesar hat ein fatales Problem: es gibt nur 25 mögliche Schlüssel. Ein Computer probiert die alle in Mikrosekunden durch.
Geheimtext: "KDOOR"
+1 → JCNNQ +2 → IBMMP
+3 → HALLO ← ergibt Sinn!
+4 → GZKKN +5 → FYJJM ...
Der Mensch erkennt sofort, welche Zeile sinnvolles Deutsch ist. Spätestens nach 25 Versuchen ist Schluss.
Im Deutschen ist E der häufigste Buchstabe. Der häufigste Buchstabe im Geheimtext ist also vermutlich ein verschobenes E → Schlüssel verraten.
Caesar verschiebt alle Buchstaben gleich → das Muster der Sprache bleibt sichtbar.
XOR („exklusives Oder") vergleicht zwei Bits. Ergebnis ist 1, wenn sie verschieden sind, sonst 0. In C ist das der Operator ^.
| A | B | A ^ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Gleich → 0, verschieden → 1. Das ist die ganze Regel.
Wendet man XOR mit demselben Schlüssel zweimal an, ist man wieder am Anfang:
Daten ^ Key ^ Key = Daten
Genau das brauchen wir! Einmal XOR = verschlüsseln. Nochmal dasselbe XOR = entschlüsseln. Ver- und Entschlüsseln sind dieselbe Operation.
Wir verschlüsseln den Buchstaben 'H' (ASCII 72 = 01001000) mit dem Schlüsselbyte 75 (01001011).
H 01001000 (72)
Key 01001011 (75)
XOR --------
00000011 (3)
// Geheimtext-Byte = 3
00000011 (3)
Key 01001011 (75)
XOR --------
01001000 (72 = 'H') ✓
// gleicher Key → 'H' zurück!
Weil Ver- und Entschlüsseln dasselbe sind, brauchen wir nur eine Funktion. Wir XOR-en jedes Byte mit einem (sich wiederholenden) Schlüssel.
#include <stdio.h>
#include <string.h>
// XOR-t jedes Zeichen mit dem Schlüssel (der sich wiederholt)
void xor_crypt(char text[], int len, char key[], int keylen) {
for (int i = 0; i < len; i++) {
text[i] = text[i] ^ key[i % keylen]; // ^ ist XOR, % wiederholt den Key
}
}
int main(void) {
char nachricht[] = "Geheim";
char key[] = "K";
xor_crypt(nachricht, strlen(nachricht), key, 1); // → Zeichensalat
xor_crypt(nachricht, strlen(nachricht), key, 1); // → "Geheim" zurück!
printf("%s\n", nachricht);
return 0;
}
Daten ^ Key ^ Key = Daten direkt in Code.
i % keylen — ist der Schlüssel kürzer als der Text, fangen wir vorne im Schlüssel wieder an. Bei einem 1-Zeichen-Key wird jedes Byte mit demselben Zeichen ge-XOR-t. Genau das ist auch das Problem — siehe nächste Slide.
Ein kurzer, sich wiederholender Schlüssel hinterlässt Muster — wie bei Caesar. Aber XOR steckt eine geniale Idee in sich.
Key „K" (1 Byte) auf 1000 Zeichen → das gleiche Muster 1000-mal. Mit Statistik (wie Häufigkeitsanalyse) wieder knackbar.
Auch: zwei gleiche Klartext-Bytes ergeben zwei gleiche Geheim-Bytes → Muster sichtbar.
Ist der Schlüssel so lang wie die Nachricht, echt zufällig und wird nur einmal benutzt → mathematisch unknackbar. Bewiesen!
Problem: so einen Riesen-Schlüssel sicher zum Empfänger bringen ist in der Praxis unmöglich.
Wir haben zwei eigene Verschlüsselungen gebaut. Beide funktionieren — beide sind aber zu schwach. Warum eigentlich?
Symmetrisch heißt: ein Schlüssel für beide Richtungen — derselbe Schlüssel ver- und entschlüsselt. Genau wie unser XOR, nur viel stärker.
Advanced Encryption Standard, seit 2001 weltweiter Standard. Verschlüsselt in Blöcken zu je 16 Bytes.
128 oder 256 Bit. Bei 256 Bit gibt es 2²⁵⁶ Schlüssel — mehr als Atome im Universum. Brute Force chancenlos.
Moderne CPUs haben AES sogar in Hardware eingebaut → Gigabytes pro Sekunde. Ideal für viele Daten.
Mit OpenSSL läuft AES über die EVP-Schnittstelle. Hier die Idee — Details im Labor. Kompilieren wie in VL 8: gcc datei.c -lcrypto.
#include <openssl/evp.h>
#include <openssl/rand.h>
// --- vereinfacht: Fehlerprüfung weggelassen, Logik im Fokus ---
unsigned char key[32]; // 32 Bytes = 256-Bit-Schlüssel
unsigned char iv[16]; // Initialisierungsvektor (Zufalls-Startwert)
RAND_bytes(key, 32); // echter Zufall — wie das Salt in VL 8!
RAND_bytes(iv, 16);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
// "Nimm AES mit 256-Bit-Schlüssel, mische die Blöcke (CBC)"
EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_EncryptUpdate(ctx, out, &len, klartext, klartext_len);
EVP_EncryptFinal_ex(ctx, out + len, &rest);
// 'out' enthält jetzt den Geheimtext
IV (Initialisierungsvektor)? Ein zufälliger Startwert, der dafür sorgt, dass dieselbe Nachricht mit demselben Schlüssel jedes Mal anderen Geheimtext ergibt. Genau wie das Salt beim Hashing (VL 8) verhindert er, dass Muster sichtbar werden. Er ist nicht geheim — wird neben dem Geheimtext gespeichert.
EVP_DecryptInit/Update/Final mit demselben Schlüssel und IV. Symmetrisch eben — ein Schlüssel, beide Richtungen.
Symmetrische Verschlüsselung ist toll — aber sie hat eine fiese Lücke. Denkt eine Minute darüber nach.
Anna will Bob eine verschlüsselte Nachricht schicken. Beide brauchen denselben Schlüssel. Anna würfelt einen aus — aber wie bekommt Bob ihn?
Sie kann den Schlüssel nur übers Internet schicken — genau das unsichere Netz, das doch das Problem war!
Um den Schlüssel sicher zu schicken, bräuchte sie … einen sicheren Kanal. Aber den wollte sie ja erst herstellen. Ein Lauscher fängt den Schlüssel ab und liest alles mit.
Könnte man Verschlüsselung so bauen, dass der Schlüssel zum Abschließen ein anderer ist als der zum Aufschließen? Dann könnte Anna den Abschließ-Schlüssel ruhig öffentlich verteilen …
Asymmetrische Verschlüsselung: jeder hat ein Schlüsselpaar — einen öffentlichen (darf jeder kennen) und einen privaten (streng geheim).
Bobs Briefkasten steht auf der Straße. Der Schlitz ist öffentlich — jeder kann einen Brief einwerfen (= verschlüsseln mit Bobs öffentlichem Schlüssel).
Aber den Briefkasten aufschließen kann nur Bob — er allein hat den privaten Schlüssel.
Was mit dem einen verschlüsselt wurde, geht nur mit dem anderen wieder auf.
Anna schickt Bob eine geheime Nachricht. Achtet darauf: Annas Schlüssel kommen gar nicht vor — nur Bobs.
Die zwei Schlüssel hängen mathematisch zusammen. Der Trick: eine Richtung ist leicht, die Rückrichtung praktisch unmöglich. Eine Einwegfunktion mit Falltür.
61 × 53 = ?
→ 3233 (in 1 Sekunde)
Multiplizieren ist kinderleicht — auch mit 300-stelligen Primzahlen.
3233 = ? × ?
→ welche zwei Primzahlen?
(ausprobieren … mühsam)
Bei riesigen Zahlen bräuchten alle Computer der Welt länger als das Alter des Universums.
Beide haben Stärken und Schwächen. Die spannende Frage: kann man nicht beide kombinieren?
| Symmetrisch (AES) | Asymmetrisch (RSA) | |
|---|---|---|
| Schlüssel | 1 gemeinsamer | 2 (öffentlich + privat) |
| Geschwindigkeit | ⚡ sehr schnell | 🐢 ~1000× langsamer |
| Schlüsseltausch | ❌ das große Problem | ✅ gelöst |
| Gut für | große Datenmengen | Schlüssel sicher austauschen |
Euer Browser macht das gerade jetzt. Der Trick: asymmetrisch nur, um einmal einen symmetrischen Schlüssel zu vereinbaren — dann schnell symmetrisch weiter.
Was, wenn man die Schlüssel vertauscht? Mit privat „verschlüsseln", mit öffentlich „entschlüsseln" — das beweist, wer etwas geschrieben hat.
Bob bildet den Hash seiner Nachricht (VL 8!) und „verschlüsselt" diesen mit seinem privaten Schlüssel → die Signatur.
Nur Bob hat den privaten Schlüssel — also kann nur Bob diese Signatur erzeugen.
Jeder nimmt Bobs öffentlichen Schlüssel, „entschlüsselt" die Signatur → bekommt den Hash. Stimmt er mit dem Hash der Nachricht überein?
→ Dann ist sie echt von Bob und unverändert.
Ihr habt heute schon dutzendfach verschlüsselt — ohne es zu merken.
Das 🔒 in der Adresszeile = HTTPS/TLS aktiv. Alles zwischen euch und der Seite ist verschlüsselt. Kein Schloss → Postkarte!
WhatsApp, Signal: nur Sender und Empfänger haben die Schlüssel. Nicht mal der Anbieter kann mitlesen.
BitLocker, FileVault, LUKS verschlüsseln eure ganze Platte mit AES. Laptop geklaut? Daten bleiben geheim.
RAND_bytes, nie rand()) — wie das Salt in VL 8VL 8 + VL 9 in einem Bild. Fragt euch immer zuerst: will ich die Daten je wieder lesen können?
Passwörter prüfen, Datei-Integrität. Einbahnstraße, kein Schlüssel.
→ bcrypt / SHA-256
Nachrichten, Dateien, Netzwerkverkehr. Hin- und Rückweg mit Schlüssel.
→ weiter fragen ↓
Symmetrisch → AES. Schnell, ein Schlüssel.
Asymmetrisch → RSA. Öffentlich + privat.
Hybrid → HTTPS/TLS. Das 🔒 im Browser.
Was ihr lesen/anschauen solltet, wenn ihr tiefer einsteigen wollt:
openssl/evp.h (AES), openssl/rsa.hcrypto_box)Schönen Feierabend! 🌙
Prof. Dr. Alexandra Mikityuk
HTW Berlin · Büro Raum 308
Tel +49 30 5019-2664
Nächste Woche: Graphen I — Netzwerke & BFS