Pozdravljeni.
Ne tako dolgo nazaj, ko sem študiral na univerzi, je potekalo predavanje v disciplini "Programske metode informacijske varnosti." Naloga je zahtevala, da ustvarimo program, ki vdela sporočilo v datoteke GIF. Odločil sem se, da bom to naredil v Javi.
V tem članku bom opisal nekaj teoretičnih točk in kako je nastal ta majhen program.
Teoretični del
format GIF
GIF (Graphics Interchange Format - format za izmenjavo slik) je format za shranjevanje grafičnih slik, zmožen shranjevanja stisnjenih podatkov brez izgube kakovosti v formatu do 256 barv. Ta format je leta 1987 (GIF87a) razvil CompuServe za prenos rastrskih slik po omrežjih. Leta 1989 je bil format spremenjen (GIF89a), dodana je bila podpora za preglednost in animacijo.
Datoteke GIF imajo strukturo blokov. Ti bloki imajo vedno fiksno dolžino (oz. je ta odvisna od nekaterih zastavic), tako da je skoraj nemogoče narediti napako, kje se kateri blok nahaja. Struktura najpreprostejše neanimirane slike GIF v formatu GIF89a:
Od vseh blokov strukture nas bo v tem primeru zanimal globalni blok palete in parametri, odgovorni za paleto:
CT — prisotnost globalne palete. Če je ta zastavica nastavljena, se mora globalna paleta začeti takoj za logično ročico zaslona.
Size — velikost palete in število barv na sliki. Vrednosti za ta parameter:
Velikosti
Število barv
Velikost palete, bajti
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Metode šifriranja
Za šifriranje sporočil v slikovnih datotekah bodo uporabljene naslednje metode:
Metoda LSB (najmanj pomembni bit).
Metoda dodajanja palete
LSB metoda - običajna metoda steganografije. Sestoji iz zamenjave zadnjih pomembnih bitov v vsebniku (v našem primeru bajtov globalne palete) z bitmi skritega sporočila.
Program bo kot del te metode uporabil zadnja dva bita v bajtih globalne palete. To pomeni, da se bo za 24-bitno sliko, kjer barvna paleta obsega tri bajte za rdečo, modro in zeleno, po vdelavi sporočila vanjo vsaka barvna komponenta spremenila za največ 3/255 gradacije. Takšna sprememba bo, prvič, nevidna ali težko opazna za človeško oko, in drugič, ne bo vidna na napravah za izhod informacij nizke kakovosti.
Količina informacij bo neposredno odvisna od velikosti palete slik. Ker je največja velikost palete 256 barv in če sta v komponento vsake barve zapisana dva bita sporočila, je največja dolžina sporočila (z največjo paleto na sliki) 192 bajtov. Ko je sporočilo vdelano v sliko, se velikost datoteke ne spremeni.
Metoda razširitve palete, ki deluje samo za strukturo GIF. Najbolj učinkovito bo na slikah z majhno paleto. Njegovo bistvo je, da poveča velikost palete in s tem zagotovi dodaten prostor za zapisovanje potrebnih bajtov namesto barvnih bajtov. Če upoštevamo, da je najmanjša velikost palete 2 barvi (6 bajtov), potem je lahko največja velikost vdelanega sporočila 256 × 3–6 = 762 bajtov. Pomanjkljivost je nizka kriptografska varnost, vdelano sporočilo je mogoče prebrati s katerim koli urejevalnikom besedil, če sporočilo ni dodatno šifrirano.
Praktični del
Oblikovanje programa
V paketu bodo vključena vsa potrebna orodja za implementacijo algoritmov šifriranja in dešifriranja com.tsarik.steganography. Ta paket vključuje vmesnik Encryptor z metodami encrypt и decrypt, Razred Binary, ki omogoča delo z bitnimi nizi, pa tudi z izjemnimi razredi UnableToEncryptException и UnableToDecryptException, ki naj bi se uporabljal v metodah vmesnika Encryptor v primeru napak pri kodiranju oziroma dekodiranju.
Glavni programski paket com.tsarik.programs.gifed bo vključeval zagonski programski razred s statično metodo main, ki vam omogoča zagon programa; razred, ki shranjuje parametre programa; in paketi z drugimi razredi.
V paketu bo predstavljena implementacija samih algoritmov com.tsarik.programs.gifed.gif razredi GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Oba razreda bosta izvajala vmesnik Encryptor.
Na podlagi strukture formata GIF lahko ustvarite splošni algoritem za vnos sporočila v slikovno paleto:
Za ugotavljanje prisotnosti sporočila v sliki je treba na začetek sporočila dodati določeno zaporedje bitov, ki jih dekoder najprej prebere in preveri pravilnost. Če se ne ujema, se šteje, da na sliki ni skritega sporočila. Nato morate določiti dolžino sporočila. Nato samo besedilo sporočila.
Diagram razredov celotne aplikacije:
Izvedba programa
Izvedbo celotnega programa lahko razdelimo na dve komponenti: implementacijo metode šifriranja vmesnika in metode dešifriranja Encryptor, v razredih GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, in implementacijo uporabniškega vmesnika.
Upoštevajte razred GIFEncryptorByLSBMethod.
polja firstLSBit и secondLSBit vsebujejo število bitov vsakega bajta slike, v katero je treba vnesti sporočilo in od koder je treba sporočilo prebrati. Polje checkSequence shrani zaporedje kontrolnih bitov, da zagotovi prepoznavanje vdelanega sporočila. Statična metoda getEncryptingFileParameters vrne parametre podane datoteke in značilnosti morebitnega sporočila.
Algoritem metode encrypt Razred GIFEncryptorByLSBMethod:
In njegova koda:
@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Input file is null");
}
if (out == null) {
throw new NullPointerException("Output file is null");
}
if (text == null) {
throw new NullPointerException("Text is null");
}
// read bytes from input file
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// check format
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToEncodeException("Input file has wrong GIF format");
}
// read palette size property from first three bits in the 10-th byte from the file
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// calculate color count and possible message length
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2;// one byte for check and one byte for message length
if (possibleTextLength < text.length()) {
throw new UnableToEncodeException("Text is too big");
}
int n = 13;
// write check sequence
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++;
}
// write text length
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++;
}
// write message
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++;
}
}
// write output file
OutputStream os = new FileOutputStream(out);
os.write(bytes);
os.close();
}
Algoritem in izvorna koda metode decrypt Razred GIFEncryptorByLSBMethod:
@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Input file is null");
}
// read bytes from input file
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// check format
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToDecodeException("Input file has wrong GIF format");
}
// read palette size property from first three bits in the 10-th byte from the file
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// calculate color count and possible message length
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // one byte for check and one byte for message length
int n = 13;
// read check sequence
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("There is no encrypted message in the image (Check sequence is incorrect)");
}
// read text length
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("Decoded text length is less than 0");
}
if (possibleTextLength < textLength) {
throw new UnableToDecodeException("There is no messages (Decoded message length (" + textLength + ") is less than Possible message length (" + possibleTextLength + "))");
}
// read text bits and make text bytes
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);
}
Izvedba razreda GIFEncryptorByPaletteExtensionMethod bo podobna, le način shranjevanja/branja informacij je drugačen.
V razredu MainFrame opisane so metode zavijanja: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), obdelava rezultatov vmesniških metod Encryptor in interakcijo z uporabnikom, tj. odpiranje pogovornega okna za izbiro datoteke, prikazovanje sporočil o napakah itd.; kot tudi druge metode: openImage(), ki uporabniku omogoča izbiro slike, exit(), ki zapusti aplikacijo. Te metode se kličejo iz Actionustrezni elementi menija. Ta razred dodatno implementira pomožne metode: createComponents() - izdelava komponent obrazca, loadImageFile(File f) — nalaganje slike v posebno komponento iz datoteke. Izvedba razreda GIFEncryptorByPaletteExtensionMethod podobno kot pri izvedbi razreda GIFEncryptorByLSBMethod, glavna razlika je v načinu zapisa in branja bajtov sporočil iz palete.
Delovanje programa
LBS metoda
Recimo, da obstaja taka slika:
Na tej sliki je paleta sestavljena iz 256 barv (kot jih shrani Paint). Prve štiri barve so: bela, črna, rdeča, zelena. Ostale barve so črne. Zaporedje bitov globalne palete bo naslednje: