Pozdravy.
Nie je to tak dávno, keď som študoval na univerzite, bol kurz v disciplíne „Softvérové metódy informačnej bezpečnosti“. Zadanie vyžadovalo, aby sme vytvorili program, ktorý vloží správu do súborov GIF. Rozhodol som sa to urobiť v Jave.
V tomto článku popíšem niektoré teoretické body, ako aj to, ako tento malý program vznikol.
Teoretická časť
vo formáte GIF
GIF (Graphics Interchange Format – formát na výmenu obrázkov) je formát na ukladanie grafických obrázkov, schopný ukladať komprimované dáta bez straty kvality vo formáte až 256 farieb. Tento formát bol vyvinutý v roku 1987 (GIF87a) spoločnosťou CompuServe na prenos rastrových obrázkov cez siete. V roku 1989 bol upravený formát (GIF89a), pridaná podpora priehľadnosti a animácie.
Súbory GIF majú blokovú štruktúru. Tieto bloky majú vždy pevnú dĺžku (alebo to závisí od niektorých príznakov), takže je takmer nemožné urobiť chybu v tom, kde sa ktorý blok nachádza. Štruktúra najjednoduchšieho neanimovaného obrázka GIF vo formáte GIF89a:
Zo všetkých blokov štruktúry nás v tomto prípade bude zaujímať globálny blok palety a parametre zodpovedné za paletu:
CT — prítomnosť globálnej palety. Ak je tento príznak nastavený, globálna paleta musí začínať hneď za logickým ovládačom obrazovky.
Size — veľkosť palety a počet farieb na obrázku. Hodnoty pre tento parameter:
Veľkosť
Počet farieb
Veľkosť palety, bajty
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Metódy šifrovania
Na šifrovanie správ v obrazových súboroch sa použijú nasledujúce metódy:
Metóda LSB (Least Significant Bit).
Metóda pridávania palety
LSB metóda - bežná metóda steganografie. Pozostáva z nahradenia posledných významných bitov v kontajneri (v našom prípade bajtov globálnej palety) bitmi skrytej správy.
Program použije posledné dva bity v bajtoch globálnej palety ako súčasť tejto metódy. To znamená, že pri 24-bitovom obrázku, kde je farba palety tri bajty pre červenú, modrú a zelenú, sa po vložení správy do nej každá farebná zložka zmení maximálne o 3/255 gradácie. Takáto zmena bude po prvé pre ľudské oko neviditeľná alebo ťažko postrehnuteľná a po druhé nebude viditeľná na zariadeniach na výstup informácií nízkej kvality.
Množstvo informácií bude priamo závisieť od veľkosti palety obrázkov. Keďže maximálna veľkosť palety je 256 farieb a ak sú do zložky každej farby zapísané dva bity správy, potom maximálna dĺžka správy (s maximálnou paletou na obrázku) je 192 bajtov. Po vložení správy do obrázka sa veľkosť súboru nezmení.
Metóda rozšírenia palety, ktorý funguje len pre štruktúru GIF. Najúčinnejšie to bude na obrázkoch s malou paletou. Jeho podstatou je, že zväčšuje veľkosť palety, čím poskytuje dodatočný priestor na zapisovanie potrebných bajtov na miesto farebných bajtov. Ak uvážime, že minimálna veľkosť palety sú 2 farby (6 bajtov), potom maximálna veľkosť vloženej správy môže byť 256 × 3–6 = 762 bajtov. Nevýhodou je nízka kryptografická bezpečnosť, vložená správa sa dá prečítať pomocou ľubovoľného textového editora, ak správa nebola podrobená dodatočnému šifrovaniu.
Praktická časť
Dizajn programu
Všetky potrebné nástroje na implementáciu šifrovacích a dešifrovacích algoritmov budú súčasťou balenia com.tsarik.steganography. Tento balík obsahuje rozhranie Encryptor s metódami encrypt и decrypt, Trieda Binary, ktorý poskytuje možnosť pracovať s bitovými poľami, ako aj s triedami výnimiek UnableToEncryptException и UnableToDecryptException, ktoré by sa mali používať v metódach rozhrania Encryptor v prípade chýb kódovania a dekódovania.
Hlavný programový balík com.tsarik.programs.gifed bude obsahovať triedu spustiteľných programov so statickou metódou main, ktorý vám umožní spustiť program; trieda, ktorá ukladá parametre programu; a balíčky s inými triedami.
Implementácia samotných algoritmov bude prezentovaná v balíku com.tsarik.programs.gifed.gif triedy GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Obe tieto triedy budú implementovať rozhranie Encryptor.
Na základe štruktúry formátu GIF môžete vytvoriť všeobecný algoritmus na vloženie správy do palety obrázkov:
Na zistenie prítomnosti správy v obrázku je potrebné na začiatok správy pridať určitú postupnosť bitov, ktoré dekodér prečíta ako prvý a skontroluje správnosť. Ak sa nezhoduje, má sa za to, že na obrázku nie je žiadna skrytá správa. Ďalej musíte zadať dĺžku správy. Potom samotný text správy.
Schéma tried celej aplikácie:
Implementácia programu
Implementáciu celého programu možno rozdeliť na dve zložky: implementáciu šifrovania rozhrania a metód dešifrovania Encryptor, v triedach GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethoda implementáciu používateľského rozhrania.
Zvážte triedu GIFEncryptorByLSBMethod.
poľa firstLSBit и secondLSBit obsahujú počty bitov každého bajtu obrázku, do ktorého sa má správa zadať a odkiaľ sa má správa prečítať. Lúka checkSequence ukladá riadiacu bitovú sekvenciu na zabezpečenie rozpoznania vloženej správy. Statická metóda getEncryptingFileParameters vráti parametre zadaného súboru a charakteristiky potenciálnej správy.
Algoritmus metódy encrypt trieda GIFEncryptorByLSBMethod:
A jeho kód:
@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();
}
Algoritmus a zdrojový kód metódy decrypt trieda 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);
}
Realizácia triedy GIFEncryptorByPaletteExtensionMethod budú podobné, len spôsob ukladania/čítania informácií je odlišný.
V triede MainFrame metódy balenia sú opísané: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), spracovanie výsledkov metód rozhrania Encryptor a interakciu s používateľom, t.j. otvorenie dialógového okna na výber súboru, zobrazenie chybových hlásení atď.; ako aj iné metódy: openImage(), čo umožňuje používateľovi vybrať obrázok, exit(), ktorý ukončí aplikáciu. Tieto metódy sa volajú z Actionzodpovedajúce položky ponuky. Táto trieda navyše implementuje pomocné metódy: createComponents() - tvorba komponentov formulára, loadImageFile(File f) — načítanie obrázka do špeciálneho komponentu zo súboru. Realizácia triedy GIFEncryptorByPaletteExtensionMethod podobne ako pri implementácii triedy GIFEncryptorByLSBMethod, hlavný rozdiel je v spôsobe, akým sa bajty správy zapisujú a čítajú z palety.
Prevádzka programu
LBS metóda
Povedzme, že existuje takýto obrázok:
Na tomto obrázku sa paleta skladá z 256 farieb (ako Paint ukladá). Prvé štyri farby sú: biela, čierna, červená, zelená. Ostatné farby sú čierne. Globálna bitová sekvencia palety bude nasledovná: