-
Notifications
You must be signed in to change notification settings - Fork 0
ImageSteg
Diese Klasse (nebst Unterklassen) dient zur steganographischen Verschlüsselung von Nachrichten in verlustfreien Bildformaten (PNG, BMP, GIF). Nachrichten werden bei der Enkodierung als Bytearray überreicht und bei der Dekodierung als solches zurückgegeben, ihr Inhalt spielt keine Rolle.
Die Klasse erkennt das Format des überreichten Bildes und gibt das Bild nach erfolgter Bearbeitung im selben Format als Bytearray zurück. Je nach Format wird aus zwei unterschiedlichen Algorithmen zur Verschlüsselung gewählt:
Format | Bittiefe | Algorithmus |
---|---|---|
PNG | 24 - 32 Bit | RandomLSB |
BMP (intransparent) | 24 - 32 Bit | RandomLSB |
GIF | 8 Bit (indexed) | ColorCouples |
Andere Formate werden nicht unterstützt. BMP mit transparenten Pixeln werden nicht unterstützt (siehe Erklärungen).
Die verwendeten Algorithmen wurden im Rahmen dieses Projektes entwickelt. Die Erklärungen zu ihrer Funktionsweise und die Implementationen finden sich unter den folgenden Links:
Funktionsweise | Implementation |
---|---|
RandomLSB | PixelBit |
ColorCouples | PixelIndex |
Die Methoden decode(...)
und encode(...)
können jeweils mit oder ohne Übergabe eines Seeds verwendet werden. Der Seed beeinflusst die pseudorandomisierte Auswahl der für die Verschlüsselung verwendeten Pixel (siehe Erklärungen). Sein Wert (oder seine Nicht-Verwendung) muss also bei En- und Dekodierung übereinstimmen und kann daher als eine Art Passwort gesehen werden. Wird kein Seed explizit angegeben, verwendet ImageSteg stattdessen einen (festen) Standard-Seed.
Bei Erstellung eines Objektes der Klasse ImageSteg können dem Konstruktor zwei Optionen (Boolean) übergeben werden, deren Bedeutung nachfolgend erklärt wird. Die Nutzung des default-Konstruktors ImageSteg()
entspricht ImageSteg(useDefaultHeader = true, useTransparent = false)
.
kurz:
true
: der versteckten Nachricht wird ein (nicht zusätzlich verschlüsselter) Header hinzugefügt, die Methodendecode(...)
undisSteganographicData(...)
können bei der Entschlüsselung verwendet werdenfalse
: die Nachricht wird unverändert verschlüsselt, bei der Dekodierung mussdecodeRaw(...)
verwendet werden.isSteganographicData(...)
gibt immerfalse
zurück.
Die versteckte Nachricht kann zusätzlich mit einem Header versehen werden, der die Identifikation dieses Projekts und die Länge der versteckten Nachricht beinhaltet. Um die Methoden decode(...)
und isSteganographicData(...)
bei der Dekodierung nutzen zu können, muss diese Option auf true
gesetzt werden.
Die Inklusion eines Headers vermindert jedoch die Sicherheit der versteckten Nachricht. Sollte ein Angreifer den verwendeten Algorithmus kennen, muss er "lediglich" alle möglichen seeds (Datentyp long) durchgehen. Sobald er den Header lesen kann, ist auch der Rest der Nachricht korrekt extrahiert worden, selbst wenn die Nachricht zusätzlich verschlüsselt wurde.
Um dies zu vermeiden, kann auf den Header verzichtet werden. Zur Dekodierung muss dann allerdings die Methode decodeRaw(...)
verwendet werden und die Länge der Nachricht (in Bytes) bekannt sein. Die Methode isSteganographicData(...)
gibt bei einem so bearbeiteten Bild immer false
zurück.
kurz:
- betrifft nur PNG
true
: vollständig transparente Pixel werden zur Verschlüsselung benutztfalse
: vollständig transparente Pixel werden nicht zur Verschlüsselung benutzt
Die o.g. Formate können vollständig durchsichtige (transparente) Pixel enthalten. Sollten auch in diesen Informationen verschlüsselt werden, wird die Kapazität des Bildes zur Aufnahme von Informationen zwar erhöht, die Verschlüsselung an sich ist dadurch jedoch auffälliger. Diese Option betrifft nur PNG. Bei einem GIF wird sie ignoriert, da sich nachfolgende Frames in animierten GIFs auf die Transparenz ihrer Vorgänger verlassen. BMP mit transparenten Pixeln werden nicht unterstützt (siehe Erklärungen).
Diese Option verändert die Auswahl der durch den Seed bestimmten Pixel. Ihr Wert muss bei En- und Dekodierung also übereinstimmen. Die Verwendung teilweise transparenter Pixel wird nicht beeinflusst.
// Variables
// -------------------------------------------------
byte[] message2Hide = "Hello World".getBytes();
byte[] imageFileAsByteArray = Files.readAllBytes(new File("/path/to/anImage.png").toPath());
Steganography steganography = new ImageSteg();
long seed = 1234L; // optional, can be used to influence how the message is hidden in the image
// Encode
// -------------------------------------------------
byte[] steganographicImage = steganography.encode(imageFileAsByteArray, message2Hide);
// or byte[] steganographicImage = steganography.encode(imageFileAsByteArray, message2Hide, seed); // to use a custom seed
// Decode
// -------------------------------------------------
byte[] hiddenMessage = steganography.decode(steganographicImage);
// or byte[] hiddenMessage = steganography.decode(steganographicImage, seed); // if a custom seed was used
// Variables
// -------------------------------------------------
byte[] message2Hide = "Hello World".getBytes();
byte[] imageFileAsByteArray = Files.readAllBytes(new File("/path/to/anImage.png").toPath());
Steganography steganography = new ImageSteg(false, true);
long seed = 1234L; // optional, can be used to influence how the message is hidden in the image
// Encode
// -------------------------------------------------
byte[] steganographicImage = steganography.encode(imageFileAsByteArray, message2Hide);
// or byte[] steganographicImage = steganography.encode(imageFileAsByteArray, message2Hide, seed); // to use a custom seed
// Decode
// -------------------------------------------------
int length = message2Hide.length; // must be known by decoding party
byte[] hiddenMessage = steganography.decodeRaw(length, steganographicImage);
// or byte[] hiddenMessage = steganography.decode(length, steganographicImage, seed); // if a custom seed was used
In diesem Projekt wird die native Java-Klasse "java.awt.image.BufferedImage" (BufferedImage) genutzt, um Nachrichten in Bildern zu verstecken. Das Einlesen und Schreiben von Bildern realisiert die Klasse "javax.imageio.ImageIO" und nutzt dabei sog. ImageReader (Reader) und ImageWriter (Writer).
Während nativ vorhandene Reader ein BMP mit transparenten Pixeln zwar einlesen können, existiert kein nativer Writer, der aus einem BufferedImage ein BMP mit transparenten Pixeln schreiben könnte. Daher werden diese nicht unterstützt.
Die Auswahl der Pixel geschieht separat von ihrer Enkodierung über sog. Overlays. Diese lösen die Pixel aus ihren x-y-Koordinaten und repräsentieren sie als sequenzielle Folge, ähnlich einem Iterator. Diese Struktur ermöglicht die unkomplizierte Erweiterung oder Veränderung der Pixelreihenfolge und -auswahl, ohne dass der enkodierende Algorithmus verändert werden muss.
Da eine Verschlüsselung in Pixeln mit sequenzieller Reihenfolge (z.B. zeilenweise von oben nach unten) das Auffinden einer versteckten Nachricht relativ einfach machen würde, werden die Pixel unter Verwendung eines Seeds pseudorandomisiert ausgewählt. Dies sorgt für eine recht gleichmäßige Verteilung veränderter Pixel im Bild.
Daraus entstehen zwei Vorteile:
- Da ein Pixel jeweils nur ein Bit repräsentiert und Inhalt sowie Format der Nachricht unbekannt sind, ist es beinahe unmöglich, die korrekte Reihenfolge ohne Kenntnis des Seeds zu bestimmen
- Statt an einer bestimmten Stelle im Bild viele Pixelveränderungen vorzunehmen, werden diese über das gesamte Bild verteilt. Dadurch wird es deutlich schwieriger, überhaupt zu erkennen, dass in dem vorliegenden Bild eine Nachricht versteckt ist
Beteiligte Interfaces / Klassen:
Eine mögliche Erweiterung dieses Moduls wäre der Ausschluss von Pixeln (größerer) gleichfarbiger Flächen. Hierfür müsste ein Overlay erschaffen werden, dass solche Flächen erkennt und Pixel daraus nicht anbietet. Der jeweilige Algorithmus kann dabei unverändert bleiben.
Nachteile:
- Da weniger Pixel genutzt werden, sinkt die Kapazität des Bildes
- Der Prozess der Flächenidentifikation ist relativ aufwändig
- Die zusätzliche Option würde das Interface oder den Konstruktor verkomplizieren
Vorteil:
Die steganographrische Bearbeitung wäre deutlich unauffälliger. Ohne diese Erweiterung können bestimmte steganalytische Verfahren dafür sorgen, dass einzelne Pixel erkannt werden können, die eine leicht andere Farbe haben als ihre direkten Nachbarn. Dadurch ist man zwar noch weit von der Entschlüsselung der versteckten Nachricht entfernt, ihre Anwesenheit würde jedoch bestätigt.
Sollten solche Farbunterschiede jedoch nur in Randbereichen von Flächen auftreten, in denen ein gewisser Farbverlauf ohnehin zu erwarten ist, wird es bedeutend schwerer, dies zu ermitteln.
- ImageSteg zur Verschlüsselung in Bildern
- AnimatedGif zur Verschlüsselung in animierten GIFs
- VideoSteg zur Verschlüsselung in Videos
- MP3Steganography zur Verschlüsselung in MP1-/MP2-/MP3-Dateien