はじめに
ご挨拶。
少し前まで、私が大学で勉強していたとき、「情報保護のソフトウェア手法」という分野で授業がありました。 割り当て時には、メッセージをGIFファイルに埋め込むプログラムを作成する必要がありました。 Javaで行うことにしました。
この記事では、この小さなプログラムがどのように作成されたかだけでなく、いくつかの理論的なポイントについて説明します。
理論部
GIF形式
GIF(Eng。Graphics Interchange Format-画像を交換するためのフォーマット)-圧縮されたデータを最大256色のフォーマットで品質を損なうことなく保存できるグラフィック画像を保存するためのフォーマット。 この形式は、ネットワーク経由でラスターイメージを送信するために、CompuServeによって1987年(GIF87a)に開発されました。 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法は、一般的なステガノグラフィ法です。 これは、コンテナの最後の重要なビット(この場合、グローバルパレットのバイト)を非表示にするメッセージのビットで置き換えることで構成されています。
プログラムは、このメソッドの一部として、グローバルパレットのバイト単位の最後の2ビットを使用します。 つまり、パレットの色が赤、青、緑の3バイトである24ビット画像の場合、メッセージが埋め込まれた後、各色成分は最大で3/255階調変化します。 そのような変化は、第一に、目に見えないか、人間の目に気付くのが困難であり、第二に、低品質の情報出力デバイスでは区別できません。
情報の量は、画像パレットのサイズに直接依存します。 パレットの最大サイズは256色であり、各色のコンポーネントにメッセージの2ビットを書き込む場合、メッセージの最大長(画像内の最大パレットを含む)は192バイトです。 画像にメッセージを埋め込んだ後、ファイルサイズは変わりません。
GIF構造に対してのみ機能するパレット拡張メソッド 。 小さなパレットの画像で最も効果的です。 その本質は、パレットのサイズを大きくし、それによって色のバイトの代わりに必要なバイトを記録するための追加のスペースを与えることです。 パレットの最小サイズが2色(6バイト)であると考える場合、埋め込みメッセージの最大サイズは256×3–6 = 762バイトになります。 欠点はセキュリティが低いことです。メッセージが追加の暗号化を受けていない場合は、テキストエディタを使用して埋め込みメッセージを読み取ることができます。
実用部
プログラム設計
暗号化および復号化アルゴリズムの実装に必要なすべてのツールは、
com.tsarik.steganography
パッケージに
com.tsarik.steganography
ます。 このパッケージには、
encrypt
decrypt
メソッドと
decrypt
メソッドを備えた
Encryptor
インターフェイス、ビット配列を操作する機能を提供する
Binary
クラス、およびエンコードおよびデコードエラーの場合にそれぞれ
Encryptor
インターフェイスメソッドで使用される
UnableToEncryptException
および
UnableToDecryptException
例外クラスが含まれます。
メインプログラムパッケージ
com.tsarik.programs.gifed
には、プログラムを実行できる静的
main
メソッドで起動されるプログラムクラスが含まれます。 プログラムパラメータを格納するクラス。 他のクラスのパッケージ。
アルゴリズム自体の実装は、
GIFEncryptorByLSBMethod
および
GIFEncryptorByPaletteExtensionMethod
com.tsarik.programs.gifed.gif
パッケージ
com.tsarik.programs.gifed.gif
提示され
GIFEncryptorByPaletteExtensionMethod
。 これらのクラスは両方とも
Encryptor
インターフェイスを実装します。
GIF形式の構造に基づいて、画像パレットにメッセージを埋め込むための一般的なアルゴリズムを作成できます。
画像内のメッセージの存在を判別するには、メッセージの先頭に特定のビットシーケンスを追加する必要があります。これは、デコーダーが最初に読み取り、正確性をチェックします。 一致しない場合は、画像に隠されたメッセージはないと見なされます。 次に、メッセージの長さを指定する必要があります。 次に、メッセージ自体のテキスト。
アプリケーション全体のクラス図:
プログラムの実施
プログラム全体の実装は、
GIFEncryptorByLSBMethod
および
GIFEncryptorByPaletteExtensionMethod
Encryptor
インターフェースの暗号化および復号化メソッドの実装、およびユーザーインターフェースの実装の2つのコンポーネントに分けることができます。
GIFEncryptorByLSBMethod
クラスを検討して
GIFEncryptorByLSBMethod
。
firstLSBit
および
secondLSBit
は、メッセージの入力先および送信元の画像の各バイトのビット番号が含まれます。
checkSequence
フィールドは、埋め込まれたメッセージの認識を保証するためにビットのチェックシーケンスを保持します。 静的メソッド
getEncryptingFileParameters
は、指定されたファイルのパラメーターと潜在的なメッセージの特性を返します。
encrypt
クラスの
encrypt
メソッドのアルゴリズム:
そしてそのコード:
@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(); }
GIFEncryptorByLSBMethod
クラスの
decrypt
メソッドのアルゴリズムとソースコード:
@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)
記述します
encryptImage(Encryptor encryptor)
インターフェースメソッドの結果を処理し、ユーザーと対話する、つまりファイル選択ダイアログを開く、エラーメッセージを表示するなど、
decryptImage(Encryptor encryptor)
encryptImage(Encryptor encryptor)
および
decryptImage(Encryptor encryptor)
です。 ; 他のメソッド:
openImage()
、ユーザーが画像を選択できるようにする、
exit()
、アプリケーションを終了する これらのメソッドは、対応するメニュー項目のアクションから呼び出されます。 このクラスでは、補助メソッドが追加で実装されます
createComponents()
-フォームコンポーネントの作成、
loadImageFile(File f)
-ファイルから特別なコンポーネントに画像をロードします。
GIFEncryptorByPaletteExtensionMethod
クラスの実装は
GIFEncryptorByPaletteExtensionMethod
クラスの実装に似ていますが、主な違いは、メッセージバイトがパレットで読み書きされる方法にあります。
プログラム作業
LBS法
そのような画像があるとしましょう:
この画像では、パレットは256色で構成されています(これがペイントの保存方法です)。 最初の4色:白、黒、赤、緑。 残りの色は黒です。 グローバルパレットのビットシーケンスは次のようになります。
111111 11 111111 11 111111 11 000 000 00 000 000 00 000 000 00 111111 11 000 000 00 000 000 00 000 000 00 111111 11 000 000 00 ...
メッセージの埋め込み後、下線付きのビットはメッセージのビットに置き換えられます。 結果の画像は、元の画像とほとんど変わりません。
オリジナル | メッセージが埋め込まれた画像 |
---|---|
パレット拡張方法
このメソッドを使用してメッセージが配置されている画像を開くと、次の画像を見つけることができます。
このような方法は本格的なスパイ活動では機能せず、追加のメッセージ暗号化が必要になる場合があることは明らかです。
アニメーション画像の暗号化/復号化は、通常の静的画像と同様に機能しますが、アニメーションは破損しません。
使用されたソース:
- http://ru.wikipedia.org/wiki/Gif
- http://ru.wikipedia.org/wiki/Steganography
- http://home.onego.ru/~chiezo/gif.htm
ダウンロード: