Inledning
HĂ€lsningar.
För inte sÄ lÀnge sedan, nÀr jag studerade pÄ universitetet, hade jag en kurs i Àmnet "Programvara metoder för att skydda information". Uppdraget krÀvde att skapa ett program som skulle bÀdda in ett meddelande i GIF-filer. BestÀmde mig för att göra det i Java.
I den hÀr artikeln kommer jag att beskriva nÄgra teoretiska punkter, samt hur detta lilla program skapades.
Teoretisk del
GIF-format
GIF (Graphics Interchange Format) Àr ett format för lagring av grafiska bilder, som kan lagra komprimerad data utan kvalitetsförlust i ett format pÄ upp till 256 fÀrger. Detta format utvecklades 1987 (GIF87a) av CompuServe för att överföra rasterbilder över nÀtverk. 1989 modifierades formatet (GIF89a), vilket gav stöd för transparens och animering.
GIF-filer har en blockstruktur. Dessa block har alltid en fast lÀngd (eller sÄ beror det pÄ vissa flaggor), sÄ det Àr nÀstan omöjligt att göra ett misstag om var vilket block ligger. Strukturen för den enklaste icke-animerade GIF-bilden i GIF89a-format:

Av alla block i strukturen kommer vi i det hÀr fallet att vara intresserade av det globala palettblocket och parametrarna som ansvarar för paletten:
CT- nĂ€rvaron av en global palett. Om denna flagga Ă€r instĂ€lld mĂ„ste den globala paletten börja omedelbart efter den logiska skĂ€rmbeskrivningen.Sizeâ storleken pĂ„ paletten och antalet fĂ€rger i bilden. VĂ€rdena för denna parameter Ă€r:
Storlek
Antal fÀrger
Palettstorlek, 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
Krypteringsmetoder
Följande metoder kommer att anvÀndas för att kryptera meddelanden i bildfiler:
- LSB-metoden (Least Significant Bit).
- PaletttillÀggsmetod
LSB-metoden â en vanlig metod för steganografi. Det bestĂ„r av att ersĂ€tta de sista signifikanta bitarna i behĂ„llaren (i vĂ„rt fall, byten i den globala paletten) med bitarna i det dolda meddelandet.
Programmet kommer att anvÀnda de tvÄ sista bitarna i den globala palettbyten inom denna metod. Detta innebÀr att för en 24-bitars bild, dÀr palettfÀrgen Àr tre byte för rött, blÄtt och grönt, efter att ett meddelande bÀddats in i den, kommer varje fÀrgkomponent att Àndras med maximalt 3/255 av en gradering. En sÄdan förÀndring kommer för det första att vara omÀrklig eller svÄr att mÀrka för det mÀnskliga ögat, och för det andra kommer den inte att kunna urskiljas pÄ enheter av lÄg kvalitet.
MÀngden information kommer direkt att bero pÄ storleken pÄ bildpaletten. Eftersom palettens maximala storlek Àr 256 fÀrger, och om tvÄ bitar av meddelandet skrivs in i komponenten i varje fÀrg, Àr meddelandets maximala lÀngd (med den maximala paletten i bilden) 192 byte. Efter att ett meddelande har bÀddats in i en bild Àndras inte filstorleken.
Palettexpansionsmetod, som bara fungerar för GIF-struktur. Det kommer att vara mest effektivt i bilder med en liten palettstorlek. Dess kĂ€rna Ă€r att den ökar palettens storlek, vilket ger extra utrymme för att skriva de nödvĂ€ndiga byten i stĂ€llet för fĂ€rgbytena. Om vi ââtar hĂ€nsyn till att palettens minsta storlek Ă€r 2 fĂ€rger (6 byte), kan den maximala storleken pĂ„ det inbĂ€ddade meddelandet vara 256Ă3â6=762 ââbyte. Nackdelen Ă€r lĂ„gt kryptografiskt skydd; det inbĂ€ddade meddelandet kan lĂ€sas med vilken textredigerare som helst, om meddelandet inte har utsatts för ytterligare kryptering.
Praktisk del
Designa ett program
Alla nödvÀndiga verktyg för att implementera krypterings- och dekrypteringsalgoritmer kommer att inkluderas i paketet. com.tsarik.steganography. Detta paket inkluderar grÀnssnittet Encryptor med metoder encrypt О decrypt, Klass Binary, som ger möjlighet att arbeta med bitarrayer, sÄvÀl som undantagsklasser UnableToEncryptException О UnableToDecryptException, som mÄste anvÀndas i grÀnssnittsmetoder Encryptor vid kodnings- respektive avkodningsfel.
Grundprogrampaket com.tsarik.programs.gifed kommer att inkludera en körbar programklass med en statisk metod main, som lÄter dig köra programmet; en klass som lagrar programparametrar; och paket med andra klasser.
Implementeringen av sjÀlva algoritmerna kommer att presenteras i paketet com.tsarik.programs.gifed.gif klasser GIFEncryptorByLSBMethod О GIFEncryptorByPaletteExtensionMethod. BÄda dessa klasser kommer att implementera grÀnssnittet Encryptor.
Baserat pÄ strukturen för GIF-formatet kan en allmÀn algoritm för att bÀdda in ett meddelande i en bildpalett skapas:

För att bestÀmma nÀrvaron av ett meddelande i en bild Àr det nödvÀndigt att lÀgga till en viss sekvens av bitar i början av meddelandet, som avkodaren lÀser först och kontrollerar om det Àr korrekt. Om det inte stÀmmer, anses det inte finnas nÄgot dolt meddelande i bilden. DÀrefter mÄste du ange lÀngden pÄ meddelandet. Sedan texten i sjÀlva meddelandet.
Klassdiagram över hela applikationen:

Genomförande av programmet
Implementeringen av hela programmet kan delas upp i tvÄ komponenter: implementeringen av kryptering och dekrypteringsmetoder för grÀnssnittet Encryptor, i klasser GIFEncryptorByLSBMethod О GIFEncryptorByPaletteExtensionMethod, och implementeringen av anvÀndargrÀnssnittet.
LÄt oss övervÀga klassen GIFEncryptorByLSBMethod.

fÀlt firstLSBit О secondLSBit innehÄlla bitnumren för varje byte i bilden i vilken meddelandet ska skrivas och frÄn vilket det ska lÀsas. FÀlt checkSequence lagrar en kontrollbitsekvens för att sÀkerstÀlla igenkÀnning av det inbÀddade meddelandet. Statisk metod getEncryptingFileParameters Returnerar parametrarna för den angivna filen och egenskaperna för det potentiella meddelandet.
Metodens algoritm encrypt klass GIFEncryptorByLSBMethod:

Och dess 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();
}
Metodens algoritm och kÀllkod decrypt klass 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);
}
Klassimplementering GIFEncryptorByPaletteExtensionMethod kommer att vara liknande, bara metoden att spara/lÀsa information skiljer sig Ät.
PĂ„ lektionen MainFrame Inpackningsmetoderna beskrivs: encryptImage(Encryptor encryptor) Đž decryptImage(Encryptor encryptor), bearbetning av resultaten av grĂ€nssnittsmetoder Encryptor och interagera med anvĂ€ndaren, dvs. öppna en filvalsdialog, visa felmeddelanden, etc.; och Ă€ven andra metoder: openImage(), lĂ„ter anvĂ€ndaren vĂ€lja en bild, exit(), som avslutar applikationen. Dessa metoder kallas frĂ„n Actionöver motsvarande menyalternativ. Den hĂ€r klassen implementerar dessutom följande hjĂ€lpmetoder: createComponents() - skapande av formulĂ€rkomponenter, loadImageFile(File f) â ladda en bild till en speciell komponent frĂ„n en fil. Klassimplementering GIFEncryptorByPaletteExtensionMethod liknande klassimplementeringen GIFEncryptorByLSBMethod, Ă€r den största skillnaden i hur meddelandebytes skrivs och lĂ€ses frĂ„n paletten.
Programdrift
LBS-metod
LÄt oss sÀga att det finns en sÄdan bild:

I den hÀr bilden bestÄr paletten av 256 fÀrger (sÄ hÀr sparar Paint dem). De fyra första fÀrgerna: vit, svart, röd, grön. Resten av fÀrgerna Àr svarta. Bitsekvensen för den globala paletten blir som följer:
11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000.

Efter att meddelandet har bÀddats in kommer de understrukna bitarna att ersÀttas med bitarna frÄn meddelandet. Den resulterande bilden gÄr nÀstan inte att skilja frÄn originalet.
Original
Bild med inbÀddat meddelande

![]()
Palettexpansionsmetod
NÀr du öppnar en bild som innehÄller ett meddelande med den hÀr metoden, kommer du att se följande bild:

Det Àr tydligt att den hÀr metoden inte kommer att fungera för fullfjÀdrad spionageverksamhet och kan krÀva ytterligare kryptering av meddelandet.
Kryptering/dekryptering i animerade bilder fungerar pÄ samma sÀtt som i vanliga statiska bilder, och animeringen Àr inte trasig.
AnvÀnda kÀllor:
nedladdning:
KĂ€lla: will.com
