Sissejuhatus
Tere tulemast.
Mõni aeg tagasi, ülikoolis õppides, tegin kursustöö teemal „Tehnilised meetodid teabe kaitsmiseks“. Ülesanne oli luua programm, mis sisestab sõnumi GIF-vormingusse failidesse. Otsustasin teha Java keeles.
Sellest artiklist kirjeldan mõned teoreetilised aspektid ning kuidas see väike programm loodi.
Teoreetiline osa
GIF-vorming
GIF (ingl. k. Graphics Interchange Format — piltide vahetamiseks loodud vorming) on graafiliste piltide salvestamise vorming, mis suudab hoida kompressitud andmeid kvaliteedikadudeta kuni 256 värvi. See vorming loodi 1987. aastal (GIF87a) CompuServe'i poolt rasterpiltide edastamiseks võrkudes. 1989. aastal muudeti vormingut (GIF89a), lisati tugi läbipaistvusele ja animatsioonile.
GIF-failidel on plokkstruktuur. Need andmeplokid on alati fikseeritud pikkusega (või sõltuvad nad teatud lipukestest), seega on praktiliselt võimatu eksida selles, kus mingi blokk asub. Lihtsaim mitteanimatsiooni GIF-pildi struktuur vormingus GIF89a:

Struktuuri kõigist plokkidest huvitavad meid antud juhul globaalse paleti plokk ja parameetrid, mis vastutavad paleti eest:
CT— globaalse paleti olemasolu. Kui see lipp on seadistatud, peab loogilise ekraani deskriptorile järgima globaalse paleti algus.Suurus— paleti suurus ja pildi värvide arv. Selle parameetri väärtused:
Suurus
Värvide arv
Paleti suurus, baitides
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Krüpteerimismeetodid
Kuna meetodite krüpteerimiseks sõnumite failidesse kasutatakse pilte:
- LSB meetod (Least Significant Bit, kõige vähem oluline bitt)
- Paleti täiendamise meetod
LSB meetod — levinud steganograafia meetod. See seisneb konteineri (meie puhul globaalse paleti baitide) viimaste oluliste bittide asendamises varjatud sõnumi bittidega.
Selle meetodi raames kasutatakse globaalse paleti baitides kahte viimast bitti. See tähendab, et 24-bitise pildi puhul, kus värvipalett koosneb kolmest baitist punase, sinise ja rohelise värvi jaoks, muutub sõnumi sisestamise järel iga värvikoostisosaga maksimaalselt 3/255 tooni. Selline muudatus on esiteks inimsilma jaoks peaaegu nähtamatu ning teiseks ei ole see eristatav madala kvaliteediga väljundi seadmetes.
Teabe hulk sõltub otseselt pildi paleti suurusest. Kuna maksimaalne paleti suurus on 256 värvi ja kui iga värvikomponendi sõnumisse sisestamiseks salvestatakse kaks bitti, siis maksimaalne sõnumi pikkus (maksimaalse paletiga pildis) on 192 baiti. Pärast sõnumi sisestamist pilti ei muutu faili suurus.
Paleti laiendamise meetod, mis töötab ainult GIF-struktuuri jaoks. See on kõige tõhusam väikeste palettide piltide puhul. Selle peamine idee on suurendada paleti suurust, suurendades seeläbi vajalikku ruumi värbiaine järjestamiseks. Arvestades, et minimaalne paleti suurus on 2 värvi (6 baiti), siis võib sisestatud sõnumi maksimaalne suurus olla 256×3–6=762 baiti. Puuduseks on madal krüptokaitse, sisestatud sõnumi saab lugeda mis tahes tekstiredaktori abil, kui sõnumit ei ole täiendavalt krüpteeritud.
Praktiline osa
Programmi projekteerimine
Kõik vajalikud tööriistad krüptimise ja dekrüptimise algoritmide teostamiseks on pakendis com.tsarik.steganography. See pakett sisaldab liidest Encryptor meetoditega encrypt ja decrypt, klass Binary, mis võimaldab töötada bitimassiividega, samuti erandiklassid UnableToEncryptException ja UnableToDecryptException, mida tuleb kasutada liidese meetodites Encryptor koodimise ja dekrüptimise vigade korral vastavalt.
Programmi põhikompakt com.tsarik.programs.gifed sisaldab käivitatavat programmi klassi koos staatilise meetodiga main, mis võimaldab programmi käivitada; klass, mis hoiab endas programmi parameetreid; ning pakette teiste klassidega.
Algoritmide reaalse rakenduse saab esitada paketis com.tsarik.programs.gifed.gif klasside GIFEncryptorByLSBMethod ja GIFEncryptorByPaletteExtensionMethod. Need kaks klassi rakendavad liidest Encryptor.
GIF formaadi struktuuri põhjal saab koostada üldise algoritmi sõnumi lisamiseks pildipaletti:

Et määrata, kas pildis on sõnum olemas, tuleb sõnumi algusesse lisada teatud bittide järjestus, mida dešifreerija loeb kõigepealt ja kontrollib selle õigsust. Kui see ei kattu, arvatakse, et pildis ei ole varjatud sõnumit. Edasi tuleb näidata sõnumi pikkus. Siis järgneb sõnumi tekst.
Kogu rakenduse klassidiagramm:

Programmi rakendamine
Kogu programmi rakenduse võib jagada kaheks osaks: krüptimise ja dekrüptimise meetodite rakendamine liideses Encryptor, klassides GIFEncryptorByLSBMethod ja GIFEncryptorByPaletteExtensionMethod, ja kasutajaliidese rakendamine.
Vaadakem klassi GIFEncryptorByLSBMethod.

Muutujad firstLSBit ja secondLSBit sisaldavad iga pildi baidi bitti numbreid, kuhu sõnum peab kirjutama ja kust see loetakse. Väli checkSequence salvestab bitise kontrolljärjestuse, et tagada sisseehitatud sõnumi tuvastamine. Statiline meetod getEncryptingFileParameters tagastab antud faili parameetrid ja potentsiaalse sõnumi omadused.
Meetodi algoritm encrypt klass GIFEncryptorByLSBMethod:

ja selle kood:
@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Sisendfail on tühi");
}
if (out == null) {
throw new NullPointerException("Väljundfail on tühi");
}
if (text == null) {
throw new NullPointerException("Tekst on tühi");
}
// loe baid sisendfailist
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// kontrolli formaati
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToEncodeException("Sisendfailil on vale GIF formaat");
}
// loe värvipaleti suuruse omadus esimesest kolmest bitist 10. baitis failist
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// arvuta värvide arv ja võimalik sõnumi pikkus
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // üks bait kontrolliks ja üks bait sõnumi pikkuseks
if (possibleTextLength < text.length()) {
throw new UnableToEncodeException("Tekst on liiga pikk");
}
int n = 13;
// kirjuta kontrolljärjestus
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++;
}
// kirjuta teksti pikkus
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++;
}
// kirjuta sõnum
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++;
}
}
// kirjuta väljundfail
OutputStream os = new FileOutputStream(out);
os.write(bytes);
os.close();
}
Meetod ja selle lähtekood decrypt klass GIFEncryptorByLSBMethod:

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Sisendfail on tühi");
}
// loe baitid sisendfailist
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// kontrolli formaati
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToDecodeException("Sisendfaili GIF formaat on vale");
}
// loe paleti suuruse omadus esimestest kolmest bittidest 10. baitides
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// arvuta värvide arv ja võimaliku sõnumi pikkus
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // üks bait kontrolliks ja üks bait sõnumi pikkuseks
int n = 13;
// loe kontrolljärjestus
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("Pildis ei ole krüpteeritud sõnumit (Kontrolljärjestus on vale)");
}
// loe teksti pikkus
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("Dekodeeritud teksti pikkus on väiksem kui 0");
}
if (possibleTextLength < textLength) {
throw new UnableToDecodeException("Sõnumeid ei ole (Dekodeeritud sõnumi pikkus (" + textLength + ") on väiksem kui võimaliku sõnumi pikkus (" + possibleTextLength + "))");
}
// loe tekstibaitid ja tee tekstibaitide massiiv
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);
}
Klassi rakendamine GIFEncryptorByPaletteExtensionMethod on sarnane, kuid erineb info salvestamise/lugemise meetodist.
Klassis MainFrame on kirjeldatud meetodid-„ümberpakendid“: encryptImage(Encryptor encryptor) ja decryptImage(Encryptor encryptor), mis töötlevad meetodite tulemusi ja suhtlevad kasutajaga, st avavad failide valimise dialooge, näitavad veateateid jne; samuti teised meetodid: Encryptor ja suhtlevad kasutajatega, avades näiteks failide valimise dialooge, näidates veateateid jms; samuti ka teised meetodid: openImage(), mis annab kasutajale võimaluse valida pildi, exit(), mis tagab rakendusest väljumise. Need meetodid kutsutakse esile vastava menüüpunkti Actionkaudu. Selles klassis on lisaks rakendatud ka abimeetodid: createComponents() — vormikomponentide loomine, loadImageFile(File f) — pildi laadimine spetsiaalsesse komponendisse failist. Klasside rakendamine GIFEncryptorByPaletteExtensionMethod on sarnane klassi rakendusele GIFEncryptorByLSBMethod, peamine erinevus seisneb sõnumi baitide lugemise ja salvestamise meetodis paleti kaudu.
Programmi töö
Meetod LBS
Oletame, et on selline pilt:

Antud pildis on paneel koostatud 256 värvist (nagu Paint säilitab). Esimesed neli värvi: valge, must, punane, roheline. Ülejäänud värvid on mustad. Globaalsete paneelide bittide järjekord on järgmine:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000…

Pärast sõnumi sisestamist asendatakse allajoonitud bitid sõnumist pärit bittidega. Saadud pilt ei eristu originaalist peaaegu üldse.
Originaal
Sõnumit sisaldav pilt

![]()
Paleti laiendamise meetod
Avades pildi, kuhu see meetodiga sõnum paigutatud, võib avastada sellise pildi:

Mõistetavalt ei sobi selline meetod täiemahuliseks spioneerimiseks ja vajab võib-olla täiendavat sõnumi krüptimist.
Krüptimine/dekrüptimine animeeritud piltides töötab nagu tavalistes staatilistes piltides, samas ei häirita animatsiooni.
Kasutatud allikad:
Laadi alla:
Allikas: habr.com
