Ողջույններ:
Ոչ վաղ անցյալում, երբ ես սովորում էի համալսարանում, կար դասընթաց «Տեղեկատվական անվտանգության ծրագրային մեթոդներ» թեմայով: Հանձնարարությունը մեզանից պահանջում էր ստեղծել ծրագիր, որը հաղորդագրություն կտեղադրի GIF ֆայլերում: Ես որոշեցի դա անել Java-ում:
Այս հոդվածում ես նկարագրելու եմ որոշ տեսական կետեր, ինչպես նաև, թե ինչպես է ստեղծվել այս փոքրիկ ծրագիրը:
Տեսական մաս
GIF ձևաչափ
GIF-ը (Graphics Interchange Format - պատկերների փոխանակման ձևաչափ) գրաֆիկական պատկերներ պահելու ձևաչափ է, որն ընդունակ է պահել սեղմված տվյալներն առանց որակի կորստի մինչև 256 գույների ձևաչափով։ Այս ձևաչափը մշակվել է 1987 թվականին (GIF87a) CompuServe-ի կողմից՝ ցանցերի միջոցով ռաստերային պատկերներ փոխանցելու համար: 1989 թվականին ձևաչափը փոփոխվել է (GIF89a), ավելացվել է թափանցիկության և անիմացիայի աջակցություն։
GIF ֆայլերը բլոկային կառուցվածք ունեն: Այս բլոկները միշտ ունեն ֆիքսված երկարություն (կամ դա կախված է որոշ դրոշներից), ուստի գրեթե անհնար է սխալվել, թե որտեղ է գտնվում յուրաքանչյուր բլոկը: Ամենապարզ ոչ անիմացիոն GIF պատկերի կառուցվածքը GIF89a ձևաչափով.
Կառուցվածքի բոլոր բլոկներից այս դեպքում մեզ կհետաքրքրի գլոբալ պալիտրա բլոկը և ներկապնակի համար պատասխանատու պարամետրերը.
CT - գլոբալ ներկապնակի առկայություն: Եթե այս դրոշը դրված է, ապա գլոբալ գունապնակը պետք է սկսվի էկրանի տրամաբանական բռնակից անմիջապես հետո:
Size — ներկապնակի չափը և նկարի գույների քանակը: Այս պարամետրի արժեքները.
չափ
Գույների քանակը
Պալիտրա չափը, բայթ
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Գաղտնագրման մեթոդներ
Պատկերային ֆայլերում հաղորդագրությունները գաղտնագրելու համար կօգտագործվեն հետևյալ մեթոդները.
LSB (Նվազագույն նշանակալի բիթ) մեթոդ
Պալիտրա ավելացման մեթոդ
LSB մեթոդ - ստեգանոգրաֆիայի ընդհանուր մեթոդ. Այն բաղկացած է կոնտեյների վերջին նշանակալի բիթերը (մեր դեպքում՝ գլոբալ գունապնակի բայթերը) թաքնված հաղորդագրության բիթերով փոխարինելուց:
Ծրագիրը որպես այս մեթոդի մաս կօգտագործի գլոբալ պալիտրա բայթերի վերջին երկու բիթերը: Սա նշանակում է, որ 24-բիթանոց պատկերի համար, որտեղ գունային գունապնակը երեք բայթ է կարմիր, կապույտ և կանաչի համար, հաղորդագրություն ներդնելուց հետո յուրաքանչյուր գունային բաղադրիչ կփոխվի առավելագույնը 3/255 աստիճանով: Նման փոփոխությունը, նախ, անտեսանելի կամ դժվար նկատելի կլինի մարդու աչքին, երկրորդ՝ տեսանելի չի լինի անորակ տեղեկատվության ելքային սարքերի վրա։
Տեղեկատվության քանակն ուղղակիորեն կախված կլինի պատկերի ներկապնակի չափից: Քանի որ ներկապնակի առավելագույն չափը 256 գույն է, և եթե հաղորդագրության երկու բիթ գրված է յուրաքանչյուր գույնի բաղադրիչի մեջ, ապա հաղորդագրության առավելագույն երկարությունը (պատկերի առավելագույն գունապնակով) 192 բայթ է: Հաղորդագրությունը պատկերի մեջ տեղադրվելուց հետո ֆայլի չափը չի փոխվում:
Պալիտրա ընդլայնման մեթոդ, որն աշխատում է միայն GIF կառուցվածքի համար։ Այն առավել արդյունավետ կլինի փոքր գունապնակ ունեցող պատկերների վրա: Դրա էությունը կայանում է նրանում, որ այն մեծացնում է ներկապնակի չափը, դրանով իսկ լրացուցիչ տարածք տրամադրելով գունային բայթերի փոխարեն անհրաժեշտ բայթերը գրելու համար: Եթե հաշվի առնենք, որ ներկապնակի նվազագույն չափը 2 գույն է (6 բայթ), ապա ներկառուցված հաղորդագրության առավելագույն չափը կարող է լինել 256 × 3–6 = 762 բայթ։ Թերությունը ցածր ծածկագրային անվտանգությունն է. ներկառուցված հաղորդագրությունը կարելի է կարդալ ցանկացած տեքստային խմբագրիչի միջոցով, եթե հաղորդագրությունը չի ենթարկվել լրացուցիչ կոդավորման:
Գործնական մասը
Ծրագրի ձևավորում
Փաթեթում կներառվեն գաղտնագրման և վերծանման ալգորիթմների ներդրման համար անհրաժեշտ բոլոր գործիքները com.tsarik.steganography. Այս փաթեթը ներառում է ինտերֆեյսը Encryptor մեթոդներով encrypt и decrypt, Դաս Binary, որն ապահովում է բիթային զանգվածների, ինչպես նաև բացառությունների դասերի հետ աշխատելու հնարավորություն UnableToEncryptException и UnableToDecryptException, որը պետք է օգտագործվի ինտերֆեյսի մեթոդներում Encryptor համապատասխանաբար կոդավորման և վերծանման սխալների դեպքում։
Հիմնական ծրագրի փաթեթ com.tsarik.programs.gifed կներառի runnable ծրագրի դաս՝ ստատիկ մեթոդով main, որը թույլ է տալիս գործարկել ծրագիրը; դաս, որը պահպանում է ծրագրի պարամետրերը. և փաթեթներ այլ դասերի հետ:
Փաթեթում կներկայացվի բուն ալգորիթմների իրականացումը com.tsarik.programs.gifed.gif դասեր GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Այս երկու դասերն էլ կիրականացնեն ինտերֆեյսը Encryptor.
Հիմնվելով GIF ձևաչափի կառուցվածքի վրա՝ կարող եք ստեղծել ընդհանուր ալգորիթմ՝ պատկերի ներկապնակում հաղորդագրություն ներմուծելու համար.
Պատկերում հաղորդագրության առկայությունը որոշելու համար անհրաժեշտ է հաղորդագրության սկզբում ավելացնել բիթերի որոշակի հաջորդականություն, որը նախ կարդում է ապակոդավորիչը և ստուգում դրանց ճշգրտությունը։ Եթե այն չի համընկնում, ապա համարվում է, որ պատկերում թաքնված հաղորդագրություն չկա։ Հաջորդը դուք պետք է նշեք հաղորդագրության երկարությունը: Այնուհետև հենց հաղորդագրության տեքստը:
Ամբողջ դիմումի դասի դիագրամ.
Ծրագրի իրականացում
Ամբողջ ծրագրի իրականացումը կարելի է բաժանել երկու բաղադրիչի՝ ինտերֆեյսի գաղտնագրման և գաղտնազերծման մեթոդների իրականացում Encryptor, դասարաններում GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, և օգտագործողի միջերեսի իրականացումը:
Հաշվի առեք դասը GIFEncryptorByLSBMethod.
դաշտերը firstLSBit и secondLSBit պարունակում է պատկերի յուրաքանչյուր բայթի բիթերի համարները, որոնց մեջ պետք է մուտքագրվի հաղորդագրությունը և որտեղից պետք է կարդալ հաղորդագրությունը: Դաշտ checkSequence պահպանում է ստուգման բիթերի հաջորդականությունը՝ ներկառուցված հաղորդագրության ճանաչումը ապահովելու համար: Ստատիկ մեթոդ getEncryptingFileParameters վերադարձնում է նշված ֆայլի պարամետրերը և հնարավոր հաղորդագրության բնութագրերը:
@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();
}
Մեթոդի ալգորիթմը և աղբյուրի կոդը decrypt դաս 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);
}
Դասի իրականացում GIFEncryptorByPaletteExtensionMethod նման կլինի, միայն տեղեկատվության պահպանման/ընթերցման եղանակն է տարբեր։
Դասարանում MainFrame փաթաթման մեթոդները նկարագրված են. encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), ինտերֆեյսի մեթոդների արդյունքների մշակում Encryptor և շփվել օգտագործողի հետ, այսինքն՝ բացել ֆայլի ընտրության երկխոսություն, ցույց տալ սխալ հաղորդագրություններ և այլն; ինչպես նաև այլ մեթոդներ. openImage(), թույլ տալով օգտվողին ընտրել պատկեր, exit(), որը դուրս է գալիս հավելվածից: Այս մեթոդները կոչվում են Action-ի համապատասխան մենյուի տարրերը: Այս դասը լրացուցիչ իրականացնում է օժանդակ մեթոդներ. createComponents() - ձևի բաղադրիչների ստեղծում, loadImageFile(File f) — պատկերի բեռնում ֆայլից հատուկ բաղադրիչի մեջ: Դասի իրականացում GIFEncryptorByPaletteExtensionMethod նման է դասի իրականացմանը GIFEncryptorByLSBMethod, հիմնական տարբերությունն այն է, թե ինչպես են հաղորդագրության բայթերը գրվում և ընթերցվում ներկապնակից։
Ծրագրի շահագործում
LBS մեթոդ
Ենթադրենք, կա այսպիսի պատկեր.
Այս պատկերում ներկապնակը բաղկացած է 256 գույնից (ինչպես պահպանում է Paint-ը): Առաջին չորս գույներն են՝ սպիտակ, սև, կարմիր, կանաչ։ Մնացած գույները սև են։ Համաշխարհային պալիտրա բիթերի հաջորդականությունը կլինի հետևյալը.