Uvod
Pozdrav.
Ne tako davno, dok sam studirao na sveučilištu, postojao je kolegij iz discipline "Softverske metode informacijske sigurnosti". Zadatak je od nas zahtijevao izradu programa koji ugrađuje poruku u GIF datoteke. Odlučio sam to učiniti u Javi.
U ovom ću članku opisati neke teorijske točke, kao i kako je nastao ovaj mali program.
Teorijski dio
GIF format
GIF (Graphics Interchange Format - format za razmjenu slika) je format za pohranjivanje grafičkih slika, sposoban za pohranu komprimiranih podataka bez gubitka kvalitete u formatu do 256 boja. Ovaj je format 1987. (GIF87a) razvio CompuServe za prijenos rasterskih slika preko mreža. Godine 1989. format je modificiran (GIF89a), dodana je podrška za transparentnost i animaciju.
GIF datoteke imaju blok strukturu. Ovi blokovi uvijek imaju fiksnu duljinu (ili to ovisi o nekim oznakama), tako da je gotovo nemoguće pogriješiti gdje se koji blok nalazi. Struktura najjednostavnije neanimirane GIF slike u formatu GIF89a:

Od svih blokova strukture, u ovom slučaju će nas zanimati blok globalne palete i parametri odgovorni za paletu:
CT— prisutnost globalne palete. Ako je ova zastavica postavljena, globalna paleta mora započeti odmah nakon logičkog rukovatelja zaslona.Size— veličina palete i broj boja na slici. Vrijednosti za ovaj parametar:
Veličina
Broj boja
Veličina palete, bajtovi
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 poruka u slikovnim datotekama koristit će se sljedeće metode:
- LSB (Least Significant Bit) metoda
- Metoda dodavanja palete
LSB metoda - uobičajena metoda steganografije. Sastoji se od zamjene zadnjih značajnih bitova u spremniku (u našem slučaju, bajtova globalne palete) s bitovima skrivene poruke.
Program će koristiti zadnja dva bita u bajtovima globalne palete kao dio ove metode. To znači da će se za 24-bitnu sliku, gdje je paleta boja tri bajta za crvenu, plavu i zelenu, nakon ugrađivanja poruke u nju, svaka komponenta boje promijeniti za najviše 3/255 stupnjeva. Takva će promjena, prvo, biti nevidljiva ili teško uočljiva ljudskom oku, a drugo, neće biti vidljiva na uređajima za izlaz informacija niske kvalitete.
Količina informacija izravno će ovisiti o veličini palete slika. Budući da je maksimalna veličina palete 256 boja, a ako su dva bita poruke upisana u komponentu svake boje, tada je maksimalna duljina poruke (s maksimalnom paletom na slici) 192 bajta. Nakon što je poruka ugrađena u sliku, veličina datoteke se ne mijenja.
Metoda proširenja palete, koji radi samo za GIF strukturu. Najučinkovitije će biti na slikama s malom paletom. Njegova bit je da povećava veličinu palete, čime se osigurava dodatni prostor za upisivanje potrebnih bajtova umjesto bajtova boja. Ako uzmemo u obzir da je minimalna veličina palete 2 boje (6 bajtova), tada maksimalna veličina ugrađene poruke može biti 256 × 3–6 = 762 bajta. Nedostatak je niska kriptografska sigurnost; ugrađena poruka se može čitati pomoću bilo kojeg uređivača teksta ako poruka nije podvrgnuta dodatnoj enkripciji.
Praktični dio
Dizajn programa
Svi potrebni alati za implementaciju algoritama enkripcije i dešifriranja bit će uključeni u paket com.tsarik.steganography. Ovaj paket uključuje sučelje Encryptor s metodama encrypt и decrypt, Razred Binary, koji pruža mogućnost rada s nizovima bitova, kao i klasama izuzetaka UnableToEncryptException и UnableToDecryptException, koji bi se trebao koristiti u metodama sučelja Encryptor u slučaju grešaka kodiranja odnosno dekodiranja.
Glavni programski paket com.tsarik.programs.gifed će uključivati klasu programa koja se može izvoditi sa statičkom metodom main, omogućujući vam pokretanje programa; klasa koja pohranjuje parametre programa; i paketi s drugim razredima.
U paketu će biti predstavljena implementacija samih algoritama com.tsarik.programs.gifed.gif klase GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Obje ove klase će implementirati sučelje Encryptor.
Na temelju strukture GIF formata možete stvoriti opći algoritam za uvođenje poruke u paletu slika:

Za utvrđivanje prisutnosti poruke u slici potrebno je na početak poruke dodati određeni niz bitova koje dekoder prvo čita i provjerava ispravnost. Ako se ne podudara, smatra se da na slici nema skrivene poruke. Zatim morate odrediti duljinu poruke. Zatim sam tekst poruke.
Dijagram klasa cijele aplikacije:

Provedba programa
Implementacija cjelokupnog programa može se podijeliti u dvije komponente: implementacija enkripcije sučelja i metode dešifriranja Encryptor, u razredima GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, te implementacija korisničkog sučelja.
Razmotrite klasu GIFEncryptorByLSBMethod.

Polja firstLSBit и secondLSBit sadrže brojeve bitova svakog bajta slike u koju treba unijeti poruku i odakle poruku treba pročitati. Polje checkSequence pohranjuje niz bitova za provjeru kako bi se osiguralo prepoznavanje ugrađene poruke. Statička metoda getEncryptingFileParameters vraća parametre navedene datoteke i karakteristike potencijalne poruke.
Algoritam metode encrypt razred GIFEncryptorByLSBMethod:

I njegov kod:
@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();
}
Algoritam i izvorni kod 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);
}
Implementacija klase GIFEncryptorByPaletteExtensionMethod bit će slični, samo je način spremanja/čitanja informacija drugačiji.
U klasi MainFrame opisane su metode omotača: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), obrada rezultata metoda sučelja Encryptor i interakcija s korisnikom, tj. otvaranje dijaloga za odabir datoteke, prikazivanje poruka o pogreškama, itd.; kao i druge metode: openImage(), dopuštajući korisniku odabir slike, exit(), koji izlazi iz aplikacije. Ove metode se pozivaju iz Actionodgovarajuće stavke izbornika. Ova klasa dodatno implementira pomoćne metode: createComponents() - stvaranje komponenti forme, loadImageFile(File f) — učitavanje slike u posebnu komponentu iz datoteke. Implementacija klase GIFEncryptorByPaletteExtensionMethod slično implementaciji klase GIFEncryptorByLSBMethod, glavna razlika je u načinu na koji se bajtovi poruke pišu i čitaju iz palete.
Rad programa
LBS metoda
Recimo da postoji ovakva slika:

Na ovoj slici, paleta se sastoji od 256 boja (koliko Paint sprema). Prve četiri boje su: bijela, crna, crvena, zelena. Ostale boje su crne. Globalna sekvenca bitova palete bit će sljedeća:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000...

Nakon što je poruka ugrađena, podcrtani bitovi bit će zamijenjeni bitovima iz poruke. Dobivena slika gotovo se ne razlikuje od izvornika.
Original
Slika s ugrađenom porukom

![]()
Metoda proširenja palete
Kada ovom metodom otvorite sliku koja sadrži poruku, vidjet ćete sljedeću sliku:

Jasno je da ova metoda neće raditi za potpune špijunske aktivnosti i može zahtijevati dodatno šifriranje poruke.
Šifriranje/dešifriranje u animiranim slikama radi isto kao i u običnim statičnim slikama, ali animacija nije prekinuta.
Korišteni izvori:
preuzimanje:
Izvor: www.habr.com
