Inleiding
Hallo.
Niet zo lang geleden, tijdens mijn studie aan de universiteit, had ik een project over het onderwerp 'Programmeermethoden voor informatiebeveiliging'. De opdracht vereiste het maken van een programma dat berichten in GIF-bestanden inbedde. Ik besloot het in Java te doen.
In dit artikel zal ik enkele theoretische aspecten beschrijven, evenals hoe dit kleine programma werd ontwikkeld.
Theoretisch gedeelte
GIF-formaat
GIF (Engels: Graphics Interchange Format) ā een formaat voor het opslaan van grafische afbeeldingen dat gecomprimeerde data kan opslaan zonder kwaliteitsverlies in een palet van maximaal 256 kleuren. Dit formaat werd in 1987 (GIF87a) ontwikkeld door CompuServe voor de transmissie van rasterafbeeldingen via netwerken. In 1989 werd het formaat gemodificeerd (GIF89a), met toevoegingen voor transparantie en animatie.
GIF-bestanden hebben een block-structuur. Deze blokken hebben altijd een vaste lengte (of deze is afhankelijk van bepaalde vlaggen), waardoor het praktisch onmogelijk is om een fout te maken in de locatie van een blok. De structuur van een eenvoudig niet-geanimeerd GIF-afbeelding in GIF89a-formaat:

Van alle blokken in deze structuur zullen we in dit geval geĆÆnteresseerd zijn in het globale palet en de parameters die verantwoordelijk zijn voor het palet:
CTā de aanwezigheid van een globaal palet. Als deze vlag is ingesteld, moet er direct na de beschrijving van het logische scherm een globaal palet beginnen.Grootteā de grootte van het palet en het aantal kleuren in de afbeelding. De waarden van deze parameter zijn:
Grootte
Aantal kleuren
Grootte van het palet, bytes
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Versleutelmethoden
Als versleutelmethoden voor berichten in afbeeldingsbestanden zullen worden gebruikt:
- LSB-methode (Least Significant Bit)
- Palet-aanvullingsmethode
LSB-methode ā een veelgebruikte methode van steganografie. Het houdt in dat de laatste significante bits in de container (in ons geval de bytes van het globale palet) worden vervangen door de bits van het verborgen bericht.
In het programma worden binnen deze methode de laatste twee bits in de bytes van de globale palet gebruikt. Dit betekent dat voor een 24-bits afbeelding, waarbij de kleurenpalet bestaat uit drie bytes voor rood, blauw en groen, na het inbedden van een bericht elke kleurcomponent maximaal met 3/255 kan veranderen. Deze verandering zal ten eerste onopgemerkt of moeilijk waarneembaar zijn voor het menselijk oog, en ten tweede niet waarneembaar op laagwaardige uitvoerapparaten.
De hoeveelheid informatie zal rechtstreeks afhankelijk zijn van de grootte van de afbeeldingpalet. Aangezien de maximale grootte van de palet 256 kleuren is en, als er per kleurcomponent twee bits van het bericht worden vastgelegd, de maximale lengte van het bericht (bij een maximaal palet in de afbeelding) 192 bytes bedraagt. Na het inbedden van het bericht in de afbeelding verandert de bestandsgrootte niet.
Paletuitbreidingsmethode, die alleen werkt voor de GIF-structuur. Deze zal het meest effectief zijn in afbeeldingen met kleine paletten. Het idee is om de grootte van de palet te vergroten, waardoor er extra ruimte vrijkomt om de benodigde bytes op de plaats van de kleurbits op te slaan. Gezien het feit dat de minimale grootte van de palet uit 2 kleuren (6 bytes) bestaat, kan de maximale grootte van het ingebedde bericht 256Ć3ā6=762 bytes bedragen. Het nadeel is de lage cryptografiebeveiliging; het ingebedde bericht kan eenvoudig worden gelezen met elke teksteditor, tenzij het bericht aan extra encryptie is onderworpen.
Praktisch deel
Programmaontwerp
Alle benodigde tools voor de implementatie van de encryptie- en decryptie-algoritmen zullen zich in het pakket bevinden com.tsarik.steganography. Dit pakket bevat het interface Encryptor met de methoden encrypt en decrypt, de klasse Binary, die de mogelijkheid biedt om met bitarrays te werken, evenals de exceptieklassen UnableToEncryptException en UnableToDecryptException, die moeten worden gebruikt in de methoden van het interface Encryptor in geval van fouten in codering en decodering respectievelijk.
De hoofdpakket van het programma com.tsarik.programs.gifed zal de uitvoerbare klasse van het programma bevatten met de statische methode main, waarmee het programma kan worden gestart; een klasse die de parameters van het programma bevat; en pakketten met andere klassen.
De implementatie van de algoritmen zelf zal worden gepresenteerd in het pakket com.tsarik.programs.gifed.gif klassen GIFEncryptorByLSBMethod en GIFEncryptorByPaletteExtensionMethod. Beide klassen zullen het interface implementeren Encryptor.
Op basis van de structuur van het GIF-formaat kan een algemeen algoritme voor het inbedden van een boodschap in de kleurenvloeiende afbeelding worden samengesteld:

Om de aanwezigheid van de boodschap in de afbeelding te bepalen, moet aan het begin van de boodschap een bepaalde reeks bits worden toegevoegd, die door de decoder als eerste wordt gelezen en op correctheid wordt gecontroleerd. Als deze niet overeenkomt, wordt aangenomen dat er geen verborgen boodschap in de afbeelding is. Vervolgens moet de lengte van de boodschap worden opgegeven. Daarna de tekst van de boodschap.
De klasse-diagram van de gehele applicatie:

Implementatie van het programma
De implementatie van het volledige programma kan worden onderverdeeld in twee onderdelen: de implementatie van de encryptiemethoden en decodering van het interface Encryptor, in de klassen GIFEncryptorByLSBMethod en GIFEncryptorByPaletteExtensionMethod, en de implementatie van de gebruikersinterface.
Laten we de klasse bekijken GIFEncryptorByLSBMethod.

Velden firstLSBit en secondLSBit bevatten de bitnummers van elke byte van de afbeelding, waar de boodschap moet worden opgeslagen en waar deze gelezen moet worden. Het veld checkSequence bewaart de controlebits voor het waarborgen van de herkenning van de ingebedde boodschap. De statische methode getEncryptingFileParameters geeft de parameters van het opgegeven bestand terug en de kenmerken van de potentiƫle boodschap.
Het algoritme van de methode encrypt klasse GIFEncryptorByLSBMethod:

En zijn code:
@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Invoerbestand is null");
}
if (out == null) {
throw new NullPointerException("Uitvoerbestand is null");
}
if (text == null) {
throw new NullPointerException("Tekst is null");
}
// lees bytes van invoerbestand
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// controleer formaat
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToEncodeException("Invoerbestand heeft verkeerd GIF-formaat");
}
// lees paletgrootte-eigenschap uit de eerste drie bits in de tiende byte van het bestand
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// bereken kleurenaantal en mogelijke berichtlengte
int bOrigColorCount = (int)Math.pow(2, bsize + 1);
int possibleMessageLength = bOrigColorCount * 3 / 4;
int possibleTextLength = possibleMessageLength - 2; // ƩƩn byte voor controle en ƩƩn byte voor berichtlengte
if (possibleTextLength < text.length()) {
throw new UnableToEncodeException("Tekst is te groot");
}
int n = 13;
// schrijf controlevolgorde
for (int i = 0; i < checkSequence.length / 2; i++) {
byte[] ba = Binary.toBitArray(bytes[n]);
ba[firstLSBit] = checkSequence[2 * i];
ba[secondLSBit] = checkSequence[2 * i + 1];
bytes[n] = Binary.toByte(ba);
n++;
}
// schrijf tekstlengte
byte[] cl = Binary.toBitArray((byte)text.length());
for (int i = 0; i < cl.length / 2; i++) {
byte[] ba = Binary.toBitArray(bytes[n]);
ba[firstLSBit] = cl[2 * i];
ba[secondLSBit] = cl[2 * i + 1];
bytes[n] = Binary.toByte(ba);
n++;
}
// schrijf bericht
byte[] textBytes = text.getBytes();
for (int i = 0; i < textBytes.length; i++) {
byte[] c = Binary.toBitArray(textBytes[i]);
for (int ci = 0; ci < c.length / 2; ci++) {
byte[] ba = Binary.toBitArray(bytes[n]);
ba[firstLSBit] = c[2 * ci];
ba[secondLSBit] = c[2 * ci + 1];
bytes[n] = Binary.toByte(ba);
n++;
}
}
// schrijf uitvoerbestand
OutputStream os = new FileOutputStream(out);
os.write(bytes);
os.close();
}
Het algoritme en de broncode van de methode decrypt klasse GIFEncryptorByLSBMethod:

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Invoerd bestand is null");
}
// lees bytes van invoerbestand
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// controleer formaat
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToDecodeException("Invoerd bestand heeft verkeerd GIF-formaat");
}
// lees grootte van palet eigenschap uit de eerste drie bits in de 10e byte van het bestand
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// bereken kleurenteller en mogelijke berichtlengte
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // ƩƩn byte voor controle en ƩƩn byte voor berichtlengte
int n = 13;
// lees controlevolgorde
byte[] csBits = new byte[checkSequence.length];
for (int i = 0; i < 4; i++) {
byte[] ba = Binary.toBitArray(bytes[n]);
csBits[2*i] = ba[firstLSBit];
csBits[2*i+1] = ba[secondLSBit];
n++;
}
byte cs = Binary.toByte(csBits);
if (cs != Binary.toByte(checkSequence)) {
throw new UnableToDecodeException("Er is geen versleuteld bericht in de afbeelding (Controlevolgorde is onjuist)");
}
// lees tekstlengte
byte[] cl = new byte[8];
for (int i = 0; i < 4; i++) {
byte[] ba = Binary.toBitArray(bytes[n]);
cl[2*i] = ba[firstLSBit];
cl[2*i+1] = ba[secondLSBit];
n++;
}
byte textLength = Binary.toByte(cl);
if (textLength < 0) {
throw new UnableToDecodeException("Gecodeerde tekstlengte is minder dan 0");
}
if (possibleTextLength < textLength) {
throw new UnableToDecodeException("Er zijn geen berichten (Gecodeerde berichtlengte (" + textLength + ") is minder dan mogelijke berichtlengte (" + possibleTextLength + "))");
}
// lees tekstbits en maak tekstbytes
byte[] bt = new byte[textLength];
for (int i = 0; i < bt.length; i++) {
byte[] bc = new byte[8];
for (int bci = 0; bci < bc.length/2; bci++) {
byte[] ba = Binary.toBitArray(bytes[n]);
bc[2*bci] = ba[firstLSBit];
bc[2*bci+1] = ba[secondLSBit];
n++;
}
bt[i] = Binary.toByte(bc);
}
return new String(bt);
}
Implementatie van de klasse GIFEncryptorByPaletteExtensionMethod zal vergelijkbaar zijn, het enige verschil is de methode voor het opslaan/lezen van informatie.
In de klasse MainFrame zijn wrapper-methoden beschreven: encryptImage(Encryptor encryptor) en decryptImage(Encryptor encryptor), die de resultaten van de interface-methoden verwerkt Encryptor en interactie met de gebruiker verzorgt, d.w.z. dialoogen voor het kiezen van bestanden opent, foutmeldingen toont, enz.; evenals andere methoden: openImage(), die de gebruiker de mogelijkheid geeft om een afbeelding te kiezen, exit(), die het afsluiten van de applicatie mogelijk maakt. Deze methoden worden aangeroepen vanuit Actie's bijbehorende menu-items. In deze klasse zijn ook aanvullende hulpfuncties geĆÆmplementeerd: createComponents() ā creĆ«ren van formuliercomponenten, loadImageFile(File f) ā laden van een afbeelding in een speciale component vanuit een bestand. De implementatie van de klasse GIFEncryptorByPaletteExtensionMethod is vergelijkbaar met de implementatie van de klasse GIFEncryptorByLSBMethod, het belangrijkste verschil ligt in de manier van schrijven en lezen van bytes van het bericht uit het palet.
Het werk van een programma
De LBS-methode
Stel je een afbeelding voor:

In dit beeld bestaat het palet uit 256 kleuren (zoals Paint het opslaat). De eerste vier kleuren: wit, zwart, rood, groen. De overige kleuren zijn zwart. De bitvolgorde van het globale palet is als volgt:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000ā¦

Na de integratie van het bericht worden de onderstreepte bits vervangen door de bits van het bericht. Het resulterende beeld wijkt nauwelijks af van het origineel.
Origineel
Afbeelding met geĆÆntegreerd bericht

![]()
Paletuitbreidingsmethode
Wanneer het beeld waarin het bericht met deze methode is geplaatst, wordt geopend, kan men het volgende zien:

Het is duidelijk dat deze methode voor volwaardige spionage niet geschikt is en mogelijk extra encryptie van het bericht vereist.
Encryptie/decryptie in geanimeerde afbeeldingen werkt zoals in gewone statische afbeeldingen, waarbij de animatie niet wordt verstoord.
Gebruikte bronnen:
Downloaden:
Bron: habr.com
