Programming lesson
Affine-Chiffre mit Android Studio: SDPEncryptor-Tutorial für CS6601 (Herbst 2025)
Lerne, wie du eine einfache Affine-Chiffre in einer Android-App implementierst. Dieses Tutorial begleitet dich durch die Einrichtung von Android Studio, die Fehlerbehandlung mit EditText.setError() und die Umsetzung der Verschlüsselungslogik – inspiriert von modernen Messaging-Apps und Datenschutz-T
Einführung in die Affine-Chiffre und SDPEncryptor
Im Rahmen der CS6601 Assignment 4 (Herbst 2025) entwickelst du eine Android-App namens SDPEncryptor, die Nachrichten mit einer Affine-Chiffre verschlüsselt. Die Affine-Chiffre ist eine klassische monoalphabetische Substitutionschiffre, die auf einer linearen Transformation basiert: E(x) = (a * x + b) mod m, wobei a und m teilerfremd sein müssen. In Zeiten von Datenschutz und Ende-zu-Ende-Verschlüsselung (z. B. in WhatsApp, Signal) ist das Verständnis grundlegender Verschlüsselungsmechanismen relevanter denn je. Dieses Tutorial führt dich Schritt für Schritt durch die Implementierung.
Projekt einrichten: Android Studio und Build-Konfiguration
Die Einrichtung der Entwicklungsumgebung ist ein zentrales Lernziel. Verwende Android Studio Ladybug Feature Drop | 2024.2.2 (oder aktuell). Lege ein neues Projekt mit folgenden Parametern an:
- Package-Name:
edu.gatech.seclass.sdpencryptor - Sprache: Java (Kotlin optional, aber Java wird bevorzugt unterstützt)
- Minimum SDK: API 34 (Android 14)
- Build-Konfiguration: Groovy DSL (build.gradle)
Passe die build.gradle (Module: app) wie folgt an:
android {
compileSdk 34
defaultConfig {
minSdk 34
targetSdk 34
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
testOptions {
unitTests {
includeAndroidResources true
}
}
lintOptions {
tasks.lint.enabled = false
abortOnError false
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.11.1'
}Stelle sicher, dass die compileSdk und targetSdk auf 34 gesetzt sind. Die Abhängigkeiten entsprechen den Vorgaben. Vergiss nicht, nach dem Ändern der Gradle-Dateien auf Sync Now zu klicken.
UI-Layout mit den erforderlichen Widget-IDs
Erstelle ein Layout mit den folgenden Widgets und IDs. Die genauen IDs sind für die automatische Bewertung zwingend erforderlich:
EditTextfür die Eingabe des Klartextes:@+id/plainTextEditTextfür den Schlüsselparameter a:@+id/keyAEditTextfür den Schlüsselparameter b:@+id/keyBButtonzum Verschlüsseln:@+id/encryptButtonTextViewfür das verschlüsselte Ergebnis:@+id/encryptedText
Ein mögliches Layout (vereinfacht):
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/plainText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Klartext eingeben"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<EditText
android:id="@+id/keyA"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Schlüssel a"
android:inputType="number"
app:layout_constraintTop_toBottomOf="@id/plainText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/keyB" />
<EditText
android:id="@+id/keyB"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Schlüssel b"
android:inputType="number"
app:layout_constraintTop_toBottomOf="@id/plainText"
app:layout_constraintLeft_toRightOf="@id/keyA"
app:layout_constraintRight_toRightOf="parent" />
<Button
android:id="@+id/encryptButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Verschlüsseln"
app:layout_constraintTop_toBottomOf="@id/keyA"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/encryptedText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text=""
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/encryptButton"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>Beachte: Die genauen Abstände und Anordnung sind dir überlassen, aber die IDs müssen exakt stimmen.
Fehlerbehandlung mit EditText.setError()
Ein zentrales Feature der App ist die Fehlerbehandlung. Wenn der Benutzer auf den Button drückt, müssen drei Fehlerfälle abgefangen werden:
- Leeres Klartextfeld: Zeige einen Fehler auf dem
plainText-Feld an. - Ungültiger Schlüssel a: a muss eine positive ganze Zahl sein und teilerfremd zu 26 (Anzahl der Buchstaben).
- Ungültiger Schlüssel b: b muss eine nicht-negative ganze Zahl sein.
Verwende die Methode setError(String) des EditText-Widgets. Beispiel:
if (plainText.getText().toString().isEmpty()) {
plainText.setError("Klartext darf nicht leer sein");
} else {
plainText.setError(null);
}Rufe diese Prüfungen im OnClickListener des Buttons auf. Mehrere Fehler können gleichzeitig aktiv sein – das wird durch die aufgerufenen setError-Aufrufe erreicht.
Implementierung der Affine-Chiffre
Die Affine-Chiffre arbeitet auf Buchstaben A-Z (Großbuchstaben). Kleinbuchstaben werden ignoriert oder in Großbuchstaben umgewandelt. Die Verschlüsselungsformel lautet:
c = (a * p + b) mod 26wobei p die Position des Klartextbuchstabens (A=0, B=1, ..., Z=25) und c die Position des Geheimtextbuchstabens ist. Für die Entschlüsselung wird der modulare Kehrwert von a modulo 26 benötigt. In dieser Aufgabe reicht die Verschlüsselung.
Beispiel: Mit a=5, b=8 und Klartext „HELLO“ ergibt sich:
- H (7) → (5*7+8)=43 mod 26 = 17 → R
- E (4) → (5*4+8)=28 mod 26 = 2 → C
- L (11) → (5*11+8)=63 mod 26 = 11 → L
- L (11) → 11 → L
- O (14) → (5*14+8)=78 mod 26 = 0 → A
Ergebnis: „RCLLA“.
Implementiere eine Methode String encrypt(String plainText, int a, int b), die diese Transformation durchführt. Ignoriere Zeichen, die keine Buchstaben sind, oder wirf einen Fehler – je nach Vorgabe deines Dozenten.
Unit-Tests mit JUnit und Robolectric
Die Assignment-Vorgabe schreibt vor, dass du Unit-Tests mit JUnit 4.13.2 und Robolectric 4.11.1 schreibst. Robolectric ermöglicht es, Android-Komponenten ohne Emulator zu testen. Ein einfacher Test für die Verschlüsselungsfunktion könnte so aussehen:
import org.junit.Test;
import static org.junit.Assert.*;
public class EncryptorTest {
@Test
public void testEncrypt() {
Encryptor encryptor = new Encryptor();
String result = encryptor.encrypt("HELLO", 5, 8);
assertEquals("RCLLA", result);
}
}Für UI-Tests mit Robolectric erstellst du eine Aktivität und prüfst die Fehleranzeige:
@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {
@Test
public void testEmptyPlainTextError() {
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
controller.setup();
MainActivity activity = controller.get();
Button button = activity.findViewById(R.id.encryptButton);
button.performClick();
EditText plainText = activity.findViewById(R.id.plainText);
assertNotNull(plainText.getError());
}
}Stelle sicher, dass du die Testkonfiguration in der build.gradle wie oben angegeben aktiviert hast.
Häufige Fehler und Tipps
Einige typische Stolpersteine:
- Falsche Package-Struktur: Das Hauptpaket muss
edu.gatech.seclass.sdpencryptorheißen. - Vergessene Ressourcen-IDs: Die IDs müssen exakt mit den Vorgaben übereinstimmen, sonst erkennt der Autograder die Widgets nicht.
- Fehler bei der Build-Konfiguration: Achte auf die exakte Version der Abhängigkeiten. Verwende keine neueren Versionen, da sie zu Inkompatibilitäten führen können.
- Keine Fehlerbehandlung bei leerem Text: Der Autograder erwartet, dass
setErroraufgerufen wird. Teste auch den Fall, dass beide Schlüssel ungültig sind.
Zusammenfassung und Ausblick
Mit diesem Tutorial hast du die Grundlagen für die CS6601 Assignment 4 gelegt. Du hast gelernt, wie man ein Android-Projekt mit den richtigen Einstellungen konfiguriert, eine affine Chiffre implementiert und Fehler mit EditText.setError() behandelt. Diese Fähigkeiten sind nicht nur für die Abgabe wichtig, sondern auch für das spätere Gruppenprojekt. In einer Zeit, in der Datenschutz und sichere Kommunikation immer wichtiger werden – sei es in Messaging-Apps oder bei der Verschlüsselung von Benutzerdaten – ist das Verständnis solcher Algorithmen ein wertvolles Werkzeug. Viel Erfolg bei deiner Implementierung!