Hyrje
Përshëndetje.
Së fundmi, ndërsa studionja në universitet, pata një punim për disiplinën "Metodat softuerike të mbrojtjes së informacionit". Sipas detyrës, duhej të bëja një program që integronte një mesazh në skedarët e formatit GIF. Vendosa ta realizoja atë në Java.
Në këtë artikull do të përshkruaj disa aspekte teorike, si dhe se si e krijova këtë program të vogël.
Pjesa teorike
Formati GIF
GIF (angl. Graphics Interchange Format - formati për shkëmbimin e imazheve) është një format për ruajtjen e imazheve grafike, në gjëndje të ruajë të dhëna të kompresuara pa humbje cilësie në format deri në 256 ngjyra. Ky format u zhvillua në vitin 1987 (GIF87a) nga kompania CompuServe për transmetimin e imazheve raster nëpër rrjete. Në vitin 1989, formati u modifikua (GIF89a), u shtua mbështetje për transparencën dhe animacionin.
Skedarët e formatit GIF kanë një strukturë bllokesh. Të dhënat e blloqeve kanë gjithmonë një gjatësi fikse (ose ajo varet nga disa flags), kështu që është pothuajse e pamundur të gabosh se ku ndodhet cila bllok. Struktura e një GIF-i të thjeshtë jo-animuar në formatin GIF89a:

Nga të gjithë blloqet e strukturës, në këtë rast do të na interesojnë blloku i paletës globale dhe parametrit që përgjigjen për paletën:
CTâ prania e paletĂ«s globale. NĂ«se ky flamur Ă«shtĂ« i vendosur, menjĂ«herĂ« pas pĂ«rshkruesit tĂ« ekranit logjik, duhet tĂ« fillojĂ« paleta globale.Sizeâ madhĂ«sia e paletĂ«s dhe numri i ngjyrave tĂ« imazhit. Vlerat e kĂ«tij parametri:
Size
Numri i ngjyrave
Madhësia e paletës, byte
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Metodat e enkriptimit
Si metoda për enkriptimin e mesazheve në skedarët e imazheve do të përdoren:
- Metoda LSB (Bit i Paktë më të Rendësishëm)
- Metoda e plotësimit të paletës
Metoda LSB â njĂ« metodĂ« e zakonshme e steganografisĂ«. Ajo pĂ«rfshin zĂ«vendĂ«simin e bitĂ«ve mĂ« tĂ« fundit tĂ« rĂ«ndĂ«sishĂ«m nĂ« kontejner (nĂ« kĂ«tĂ« rast bajtĂ«t e paletĂ«s globale) me bitĂ«t e mesazhit tĂ« fshehur.
Në program do të përdoren dy bita të fundit në bajtat e paletës globale. Kjo do të thotë se për një imazh 24-bit, ku ngjyra e paletës përfaqëson tre bajta për ngjyrat e kuqe, blu dhe të gjelbër, pas përfshirjes së mesazhit, çdo komponent ngjyre do të ndryshojë maksimumi në 3/255 gradacione. Ky ndryshim, së pari, do të jetë i padukshëm ose i vështirë për t'u vërejtur nga syri njerëzor, dhe së dyti, nuk do të jetë i dallueshëm në pajisjet e ulëta të cilësisë.
Sasia e informacionit do të varet drejtpërdrejt nga madhësia e paletës së imazhit. Duke qenë se madhësia maksimale e paletës është 256 ngjyra dhe, nëse regjistrohen dy bita të mesazhit në çdo komponent ngjyre, maksimumi i gjatësi së mesazhit (në paletën maksimale në imazh) është 192 bajta. Pas përfshirjes së mesazhit në imazh, madhësia e skedarit nuk ndryshon.
Metoda e zgjerimit tĂ« paletĂ«s, e cila punon vetĂ«m pĂ«r strukturĂ«n GIF. Ajo do tĂ« jetĂ« mĂ« efektive nĂ« imazhet me paleta tĂ« vogla. Thelbi i saj qĂ«ndron nĂ« rritjen e madhĂ«sisĂ« sĂ« paletĂ«s, duke i dhĂ«nĂ« kĂ«shtu hapĂ«sirĂ« tĂ« shtuar pĂ«r regjistrimin e bajtave tĂ« nevojshĂ«m nĂ« vendin e bajtave tĂ« ngjyrave. NĂ«se merret parasysh se madhĂ«sia minimale e paletĂ«s Ă«shtĂ« 2 ngjyra (6 bajta), atĂ«herĂ« madhĂ«sia maksimale e mesazhit tĂ« pĂ«rfshirĂ« mund tĂ« jetĂ« 256Ă3â6=762 bajta. Disavantazhi â siguria e ulĂ«t, mesazhi i pĂ«rfshirĂ« mund tĂ« lexohet nga çdo redaktor teksti, nĂ«se mesazhi nuk Ă«shtĂ« nĂ«ndeshur me ndonjĂ« enkriptim shtesĂ«.
Pjesa praktike
Projektimi i programit
Të gjitha mjetet e nevojshme për realizimin e algoritmeve të enkriptimit dhe dekriptimit do të gjenden në paketën com.tsarik.steganography. Kjo paketë përfshin ndërfaqen Encryptor me metodat encrypt dhe decrypt, klasa Binary, që ofron mundësinë për të punuar me arrays bitesh, si dhe klasat e përjashtimit UnableToEncryptException dhe UnableToDecryptException, që duhet të përdoren në metodat e ndërfaqes Encryptor në rast të gabimeve të kodimit dhe dekodimit përkatësisht.
Pakoja kryesore e programit com.tsarik.programs.gifed do të përfshijë klasën e ekzekutueshme të programit me metodën statike main, e cila lejon fillimin e programit; klasa që ruan parametrat e programit; dhe paketat me klasa të tjera.
Zbatimi i algoritmeve të vetë do të paraqitet në paketë com.tsarik.programs.gifed.gif klasat GIFEncryptorByLSBMethod dhe GIFEncryptorByPaletteExtensionMethod. Të dy këto klasa do të implementojnë ndërfaqen Encryptor.
Në bazë të strukturës së formatit GIF mund të përpilojmë një algoritëm të përgjithshëm për integrimin e një mesazhi në paletën e imazhit:

Për të përcaktuar praninë e mesazhit në imazh, është e nevojshme që në fillim të mesazhit të shtohet një sekuencë e caktuar bitësh, të cilën dekuruesi e lexon në radhë të parë dhe kontrollon për saktësi. Nëse ajo nuk përputhet, atëherë konsiderohet se nuk ka mesazh të fshehur në imazh. Më pas duhet të tregoni gjatësinë e mesazhit. Pas kësaj, vetë teksti i mesazhit.
Diagrami i klasave të tërë aplikacionit:

Zbatimi i programit
Zbatimi i tërë programit mund të ndahet në dy komponentë: implementimi i metodave të enkriptimit dhe dekriptimit të ndërfaqes Encryptor, në klasat GIFEncryptorByLSBMethod dhe GIFEncryptorByPaletteExtensionMethod, dhe zbatimi i ndërfaqes së përdoruesit.
Le të shqyrtojmë klasën GIFEncryptorByLSBMethod.

Fushat firstLSBit dhe secondLSBit përmbajnë numrat e bitëve të çdo byte të imazhit, në të cilat duhen vendosur dhe prej nga duhen lexuar mesazhi. Fusha checkSequence ruan sekuencën kontroluese të bitëve për sigurimin e njohjes së mesazhit të integruar. Metoda statike getEncryptingFileParameters kthen parametrat e skedarit të dhënë dhe karakteristikat e mundshme të mesazhit.
Algoritmi i metodës encrypt klasës GIFEncryptorByLSBMethod:

Dhe kodi i tij:
@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");
}
// lexoni bajtat nga skedari hyrës
byte[] bytes = new byte[(int)in.length()];
InputStream is = new FileInputStream(in);
is.read(bytes);
is.close();
// kontrolloni formatin
if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
throw new UnableToEncodeException("Input file has wrong GIF format");
}
// lexoni pronën e madhësisë së paletave nga tri bitët e para në bajtin e 10-të nga skedari
byte[] b10 = Binary.toBitArray(bytes[10]);
byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
// llogaritni numrin e ngjyrave dhe gjatësinë e mundshme të mesazhit
int bOrigColorCount = (int)Math.pow(2, bsize+1);
int possibleMessageLength = bOrigColorCount*3/4;
int possibleTextLength = possibleMessageLength-2; // një bajt për kontroll dhe një bajt për gjatësi të mesazhit
if (possibleTextLength < text.length()) {
throw new UnableToEncodeException("Text is too big");
}
int n = 13;
// shkruaj sekuencën e kontrollit
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++;
}
// shkruaj gjatësi të tekstit
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++;
}
// shkruaj mesazhin
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++;
}
}
// shkruaj skedarin e daljes
OutputStream os = new FileOutputStream(out);
os.write(bytes);
os.close();
}
Algoritmi dhe kodi burimor i metodës decrypt klasës GIFEncryptorByLSBMethod:

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
if (in == null) {
throw new NullPointerException("Input file is null");
}
// lexho 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);
}
Implementimi i klasës GIFEncryptorByPaletteExtensionMethod do të jetë i ngjashëm, vetëm se ndryshon metoda e ruajtjes/shtypjes së informacionit.
NĂ« klasĂ«n MainFrame janĂ« pĂ«rshkruar metodat-«mbĂ«shtjellĂ«se»: encryptImage(Encryptor encryptor) dhe decryptImage(Encryptor encryptor), duke pĂ«rpunuar rezultatet e metodave tĂ« interfesĂ«s Encryptor dhe duke realizuar ndĂ«rveprimin me pĂ«rdoruesin, pra hapen dialogĂ« pĂ«rzgjedhjeje skedelli, tregojnĂ« mesazhe gabimish etj.; si dhe metoda tĂ« tjera: openImage(), e cila i jep mundĂ«sinĂ« pĂ«rdoruesit tĂ« zgjedhĂ« njĂ« imazh, exit(), qĂ« realizon daljen nga aplikacioni. KĂ«to metoda thirren nga Veprimiât e pikave pĂ«rkatĂ«se tĂ« menusĂ«. NĂ« kĂ«tĂ« klasĂ« janĂ« implementuar gjithashtu metoda ndihmĂ«se: createComponents() â krijimi i komponenteve tĂ« formĂ«s, loadImageFile(File f) â ngarkimi i imazhit nĂ« njĂ« komponent tĂ« veçantĂ« nga skedari. Implementimi i klasĂ«s GIFEncryptorByPaletteExtensionMethod Ă«shtĂ« i ngjashĂ«m me implementimin e klasĂ«s GIFEncryptorByLSBMethod, dallimi kryesor qĂ«ndron nĂ« mĂ«nyrĂ«n e shkruarjes dhe leximit tĂ« bajtĂ«ve tĂ« mesazhit nga paleta.
Funksioni i programit
Metoda LBS
Njëherë le të supozojmë se ka një imazh të tillë:

Në këtë imazh, paleta përbëhet nga 256 ngjyra (në këtë mënyrë ruan Paint). Katër ngjyrat e para: e bardhë, e zezë, e kuqe, e gjelbër. Ngjyrat e tjera janë të zeza. Sekuenca e bitëve të paletës globale do të jetë si më poshtë:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000âŠ

Pas implementimit, bitët e theksuar do të zëvendësohen me bitët nga mesazhi. Imazhi i marrë ndryshon pak nga origjinali.
Originali
Imazhi me mesazhin e implementuar

![]()
Metoda e zgjerimit të paletës
Duke hapur imazhin, në të cilin është vendosur mesazhi me këtë metodë, mund të hasni një pamje të tillë:

Natyrisht, që kjo metodë nuk është e përshtatshme për aktivitete të plota spiunazhi, dhe kërkon, ndoshta, një enkriptim të mëtejshëm të mesazhit.
Enkriptimi/dekriptimi në imazhet e animuar funksionon si në imazhet statike normale, pa prishur animacionin.
Burimet e përdorura:
Shkarko:
Burimi: habr.com
