Hoşgeldiniz.
Çok uzun zaman önce, üniversitede okurken "Bilgi güvenliğinin yazılım yöntemleri" disiplininde bir ders vardı. Görev, GIF dosyalarına mesaj yerleştiren bir program oluşturmamızı gerektiriyordu. Bunu Java'da yapmaya karar verdim.
Bu yazıda bazı teorik noktaların yanı sıra bu küçük programın nasıl oluşturulduğunu anlatacağım.
Teorik kısım
GIF formatı
GIF (Grafik Değişim Formatı - görüntü alışverişi için bir format), sıkıştırılmış verileri kalite kaybı olmadan 256 renge kadar formatta depolayabilen grafik görüntüleri depolamak için bir formattır. Bu format, 1987'de (GIF87a) CompuServe tarafından taramalı görüntülerin ağlar üzerinden iletilmesi için geliştirildi. 1989'da format değiştirildi (GIF89a), şeffaflık ve animasyon desteği eklendi.
GIF dosyaları blok yapıya sahiptir. Bu blokların her zaman sabit bir uzunluğu vardır (ya da bazı bayraklara bağlıdır), dolayısıyla her bloğun nerede olduğu konusunda hata yapmak neredeyse imkansızdır. GIF89a formatındaki en basit animasyonsuz GIF görüntüsünün yapısı:
Bu durumda yapının tüm blokları arasında global palet bloğu ve paletten sorumlu parametrelerle ilgileneceğiz:
CT - küresel bir paletin varlığı. Bu bayrak ayarlanırsa genel palet mantıksal ekran tanıtıcısından hemen sonra başlamalıdır.
Size — resimdeki palet boyutu ve renk sayısı. Bu parametreye ait değerler:
beden
Renk sayısı
Palet boyutu, bayt
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Şifreleme yöntemleri
Görüntü dosyalarındaki mesajları şifrelemek için aşağıdaki yöntemler kullanılacaktır:
LSB (En Az Önemli Bit) yöntemi
Palet ekleme yöntemi
LSB yöntemi - yaygın bir steganografi yöntemi. Kaptaki son önemli bitlerin (bizim durumumuzda global palet baytları) gizli mesajın bitleriyle değiştirilmesinden oluşur.
Program bu yöntemin bir parçası olarak global palet baytlarındaki son iki biti kullanacaktır. Bu, renk paletinin kırmızı, mavi ve yeşil için üç bayt olduğu 24 bitlik bir görüntü için, içine bir mesaj yerleştirildikten sonra her renk bileşeninin maksimum 3/255 derece değişeceği anlamına gelir. Böyle bir değişiklik öncelikle insan gözüyle görülmeyecek veya fark edilmesi zor olacak, ikincisi ise düşük kaliteli bilgi çıkış cihazlarında görülmeyecek.
Bilgi miktarı doğrudan görüntü paletinin boyutuna bağlı olacaktır. Paletin maksimum boyutu 256 renk olduğundan ve her rengin bileşenine iki mesaj biti yazılırsa maksimum mesaj uzunluğu (görüntüdeki maksimum paletle birlikte) 192 bayt olur. Mesaj görsele gömüldükten sonra dosya boyutu değişmez.
Palet genişletme yöntemi, yalnızca GIF yapısı için çalışır. Küçük paletli görüntülerde en etkili olacaktır. Özü, paletin boyutunu arttırması, böylece gerekli baytların renk baytları yerine yazılması için ek alan sağlamasıdır. Paletin minimum boyutunun 2 renk (6 byte) olduğunu düşünürsek, gömülü mesajın maksimum boyutu 256 × 3–6 = 762 byte olabilir. Dezavantajı düşük kriptografik güvenliktir; eğer mesaj ek şifrelemeye tabi tutulmamışsa, gömülü mesaj herhangi bir metin düzenleyici kullanılarak okunabilir.
Pratik kısmı
Program tasarımı
Şifreleme ve şifre çözme algoritmalarını uygulamak için gerekli tüm araçlar pakete dahil edilecektir com.tsarik.steganography. Bu paket arayüzü içerir Encryptor yöntemlerle encrypt и decrypt, Sınıf Binarybit dizileriyle ve istisna sınıflarıyla çalışma yeteneği sağlar UnableToEncryptException и UnableToDecryptExceptionarayüz yöntemlerinde kullanılması gereken Encryptor sırasıyla kodlama ve kod çözme hataları durumunda.
Ana program paketi com.tsarik.programs.gifed statik yöntemle çalıştırılabilir bir program sınıfı içerecektir mainprogramı çalıştırmanıza izin verir; program parametrelerini saklayan bir sınıf; ve diğer sınıflarla paketler.
Algoritmaların uygulanması pakette sunulacaktır com.tsarik.programs.gifed.gif sınıflar GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Bu sınıfların her ikisi de arayüzü uygulayacak Encryptor.
GIF formatının yapısına dayanarak, görüntü paletine bir mesaj eklemek için genel bir algoritma oluşturabilirsiniz:
Bir görüntüde bir mesajın varlığını belirlemek için, mesajın başlangıcına, kod çözücünün ilk önce okuduğu ve doğruluğunu kontrol ettiği belirli bir bit dizisinin eklenmesi gerekir. Eşleşmiyorsa görselde gizli bir mesaj olmadığı kabul edilir. Daha sonra mesajın uzunluğunu belirtmeniz gerekir. Daha sonra mesajın metni.
Tüm uygulamanın sınıf diyagramı:
Programın uygulanması
Programın tamamının uygulanması iki bileşene ayrılabilir: arayüz şifreleme ve şifre çözme yöntemlerinin uygulanması Encryptor, sınıflarda GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethodve kullanıcı arayüzünün uygulanması.
Sınıfı düşünün GIFEncryptorByLSBMethod.
Tarla firstLSBit и secondLSBit mesajın girilmesi gereken ve mesajın nereden okunması gerektiği görüntünün her baytının bit sayısını içerir. Alan checkSequence gömülü mesajın tanınmasını sağlamak için bir kontrol biti dizisi saklar. Statik yöntem getEncryptingFileParameters belirtilen dosyanın parametrelerini ve potansiyel mesajın özelliklerini döndürür.
Yöntem algoritması encrypt sınıf GIFEncryptorByLSBMethod:
Ve onun kodu:
@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();
}
Yöntemin algoritması ve kaynak kodu decrypt sınıf 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);
}
Sınıfın uygulanması GIFEncryptorByPaletteExtensionMethod benzer olacaktır, yalnızca bilgiyi kaydetme/okuma yöntemi farklıdır.
Sınıfta MainFrame sarma yöntemleri açıklanmıştır: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), arayüz yöntemlerinin sonuçlarının işlenmesi Encryptor ve kullanıcıyla etkileşimde bulunmak, yani bir dosya seçimi iletişim kutusunu açmak, hata mesajlarını göstermek vb.; diğer yöntemlerin yanı sıra: openImage()kullanıcının bir resim seçmesine olanak tanır, exit(), uygulamadan çıkar. Bu yöntemler çağrılır Actionilgili menü öğeleri. Bu sınıf ayrıca yardımcı yöntemleri de uygular: createComponents() - form bileşenlerinin oluşturulması, loadImageFile(File f) — bir görüntünün bir dosyadan özel bir bileşene yüklenmesi. Sınıfın uygulanması GIFEncryptorByPaletteExtensionMethod sınıf uygulamasına benzer GIFEncryptorByLSBMethodtemel fark, mesaj baytlarının paletten yazılma ve okunma şeklidir.
Programın çalışması
LBS yöntemi
Diyelim ki şöyle bir görüntü var:
Bu görüntüde palet 256 renkten oluşuyor (Paint'in kaydettiği gibi). İlk dört renk: beyaz, siyah, kırmızı, yeşil. Diğer renkler siyahtır. Global palet bit dizisi aşağıdaki gibi olacaktır: