BAB V IMPLEMENTASI
5.1 Lingkungan Implementasi
5 BAB V
IMPLEMENTASI
5.1 Lingkungan Implementasi
Tahapan yang telah dikerjakan pada rancangan sistem selanjutnya diimplementasikan dengan menggunakan bahasa pemrograman C#. Bahasa pemrograman C# dianggap mampu untuk mengimplementasikan hasil rancangan yang telah dikerjakan pada bab sebelumnya.
5.2 Algoritma MEoF (Modifikasi End of File) 5.2.1 Kalkulasi lokasi penyisipan pesan
Sesuai dengan penjelasan flowchart pada Bab IV Gambar 4.2 tentang penyisipan pesan pada citra bitmap menggunakan algoritma MEoF (Modifikasi End of File), proses tahap awal adalah mengkalkulasi ukuran Byte yang dapat digunakan pada citra untuk menyisipkan pesan. Gambar 5.1 menunjukkan method GetMaxBytesSteganoCount untuk mengkalkulasi ukuran Byte citra yang dapat digunakan untuk sisipkan pesan pada citra.
Gambar 5.1 Kalkulasi lokasi penyisipan untuk ukuran width modulo 4 = 0
public static int GetMaxBytesSteganoCount(ref Bitmap sourceBmp)
1. {
2. if (sourceBmp == null || sourceBmp.PixelFormat !=
PixelFormat.Format24bppRgb) 3. {
4. return 0;
5. }
6. int width = sourceBmp.Width; 7. int height = sourceBmp.Height;
8. BitmapData bmpdata = sourceBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb); 9. int stride = bmpdata.Stride; 10. sourceBmp.UnlockBits(bmpdata); 11. if (width % 4 == 0)
12. {
13. width--;
14. }
15. return (stride - (width * 3)) * height -
TotalSteganoHeaderSize; ; 16. }
52
Pada Gambar 5.1 method GetMaxBytesSteganoCount pada baris 1 - 5 untuk pengecekan masukan citra atau format citra yang digunakan dalam menyisipkan pesan. Pada baris 6 – 14 jika format citra adalah 24 bit maka ambil ukuran width dan height dari citra, kemudian dicek dengan cara mengkalkulasi ukuran width modulo 4, jika hasilnya = 0 maka piksel terakhir digunakan sebagai lokasi untuk menyisipkan pesan. Ukuran lokasi yang dapat digunakan untuk menyisipkan pesan dikalkulasi menggunakan persamaan 3.10.
5.2.2 Lockbit data citra bitmap
Method GetBitmapData digunakan sebagai langkah awal untuk proses penyisipan pesan pada citra. Memori buffer akan digunakan untuk menampung nilai Byte citra dan pesan. Dengan demikian alokasikan memori buffer sesuai dengan ukuran citra asli Untuk itu informasi yang dibutuhkan adalah ukuran Byte citra, untuk width, height dan stride. Gambar 5.2 menunjukkan proses pengalokasian memori buffer,
Gambar 5.2 Lockbit nilai byte citra
1. private void GetBitmapData()
2. {
3. if (_steganoMode == ESteganoMode.Hide) 4. {
5. _sourceBitmap = new Bitmap(_sourceFileName); 6. _usedBitmap = MakeNewUsedBitmap(ref _sourceBitmap,
IsBitmapWidthValid BitmapData bmpData =
_usedBitmap.LockBits(new Rectangle(0, 0, _usedBitmap.Width, _usedBitmap.Height), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
7. _widthImage = _usedBitmap.Width; 8. _widthByte = bmpData.Stride; 9. heightImage = _usedBitmap.Height; 10. _usedBitmap.UnlockBits(bmpData);
11. using (MemoryStream memStream = new MemoryStream())
12. {
13. _usedBitmap.Save(memStream, ImageFormat.Bmp); 14. BytesResultBitmap = memStream.GetBuffer(); 15. }
16. }
17. MaxBytesStegano = (_widthByte - (_widthImage * 3)) * _heightImage - TotalSteganoHeaderSize;
53
Method GetBitmapData Gambar 5.2 pada baris 1 – 10 berfungsi untuk lockbit Byte citra agar lokasi memori yang digunakan untuk menanpung nilai Byte citra tidak digunakan untuk proses lainnya dan mengambil informasi ukuran untuk width, height dan stride. Sedangkan baris 11 – 16 berfungsi mengalokasikan memori buffer untuk lokasi menyalin nilai Byte citra dan pesan. Pada baris 17 - 18 untuk mengkalkulasi ukuran padding yang dapat digunakan untuk menyisipkan pesan pada citra. Padding untuk menyisipkan pesan pada citra yang memiliki ukuran width modulo 4 ≠ 0, proses untuk kalkulasi ukuran padding menggunakan persamaan 3.12.
5.2.3 Pengambilan header pesan
Untuk algoritma MEoF header pesan dialokasikan berukuran 5 Byte dengan rincian 3 Byte untuk penanda pesan dan 2 Byte untuk informasi panjang pesan yang disisipkan pada citra. Pengambilan header pesan dikerjakan pada method GetBytesHeader dengan parameter headerText, selanjutnya header pesan di-encoding untuk mendapatkan nilai Byte dari header pesan. Gambar 5.3 menunjukkan proses pengambilan header pesan dan pesan selanjutnya di-encoding ke Byte.
Gambar 5.3 Pengambilan header pesan
static byte[] GetBytesHeader(string headerText)
1. {
2. return Encoding.ASCII.GetBytes(headerText);
3. }
4. static byte[] GetBytesHeader(string headerText)
5. {
6. return Encoding.ASCII.GetBytes(headerText); 7. } 8. void GetBytesBitmapStegano() 9. { 10. GetBitmapData(); 11. IsSuccess = false; 12. 13. if (BytesResultBitmap == null) 14. {
15. AppMessageBox.ErrorMessage("Format image salah."); 16. return; }
17. if (BytesMessage.Length > MaxBytesStegano) 18. {
19. AppMessageBox.ErrorMessage(ResourceMessage.PesanTerlaluPanjang);
54
Method GetBytesHeader pada Gambar 5.3 baris 1 – 7 berfungsi untuk mengambil header pesan selanjutnya header pesan di-encoding menjadi nilai Byte. Proses pengambilan menggunakan method GetBytesHeader dengan parameter headerText. Baris 8 – 20 dengan method GetBytesBitmapStegano untuk melakukan pengecekan apakah citra yang digunakan dapat disisipkan pesan. proses pengecekan ditunjukkan pada baris ke – 17, adapun prosesnya dikerjakan dengan membandingkan lokasi citra yang dapat disisipi pesan dibandingkan dengan panjang pesan yang akan disisipkan. Jika panjang pesan lebih panjang dibandingkan dengan ukuran lokasi Byte untuk penyisipan maka tampilkan pesan kesalahan.
5.2.4 Pengukuran panjang pesan
Sebelum proses Gambar 5.3 baris ke – 17 dikerjakan, maka dibutuhkan informasi panjang pesan yang akan disisipkan pada citra. Informasi panjang pesan dapat diperoleh setelah pengguna memasukan pesan pada text editor. Panjang pesan yang dimasukan pada text editor dikalkulasi menfaatkan fungsi Length. Gambar 5.4 menunjukkan proses pengukuran panjang pesan yang dimasukan pada text editor oleh pengguna,
Gambar 5.4 Pengukuran panjang pesan
Method messageTextBox_TextChanged pada baris 1 – 8 berfungsi untuk mengukur panjang pesan yang telah dimasukan oleh pengguna, pengukuran dapat dikerjakan jika editor pesan tidak bernilai null, jika text editor tidak bernilai null maka Gambar 5.4 baris ke – 5 yaitu pengukuran panjang pesan dikerjakan.
private void messageTextBox_TextChanged(object sender, EventArgs e)
1. {
2. TextBox textBox = sender as TextBox;
3. if (textBox != null)
4. {
5. int charCount = textBox.Text.Length;
6. charCountLabel.Text = string.Format("Character Count : {0}", charCount);
7. }
55
5.2.5 Enkripsi pesan
Sebelum pesan disisipkan pada citra, dimasukan plaintext ke text editor kemudian plaintext dienkripsi dengan algoritma Rijndael menggunakan kunci 128 bit. Proses untuk enkripsi menggunakan function encrypt yang telah disediakan oleh tools C#. Gambar 5.5 menunjukkan proses enkripsi plaintext menggunakan algoritma Rijndael.
Gambar 5.5 Enkripsi pesan dengan Rijndael
Class MyRijndael adalah class yang telah disedikan oleh C# untuk melakukan enkripsi karakter ASCII menjadi Byte cipher. Gambar 5.5 baris 1-3 adalah proses enkripsi yang di-set menggunakan kunci 128 bit, dengan demikian untuk setiap blok pesan akan dibentuk menjadi 16 Byte. Baris 4 – 12 adalah proses enkripsi pesan yang dilakukan dengan method EncryptStringToBytes
public static class MyRijndael
1. {
2. public static byte[] BytesKey { get; set; }
3. private static readonly byte[] BytesIv = new byte[16];
4. public static byte[] EncryptStringToBytes(string plainText)
5. {
6. if (plainText == null || plainText.Length <= 0)
7. throw new ArgumentNullException("plainText");
8. if (BytesKey == null || BytesKey.Length <= 0)
9. throw new ArgumentNullException("BytesKey");
10. if (BytesIv == null || BytesIv.Length <= 0)
11. throw new ArgumentNullException("BytesIv");
12. byte[] encrypted;
13. using (Rijndael myRijndael = Rijndael.Create())
14. {
15. myRijndael.Key = BytesKey; 16. myRijndael.IV = BytesIv;
17. myRijndael.Padding = PaddingMode.Zeros; 18. ICryptoTransform encryptor =
myRijndael.CreateEncryptor(myRijndael.Key, myRijndael.IV);
19. using (MemoryStream msEncrypt = new MemoryStream())
20. {
21. using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor, CryptoStreamMode.Write)) 22. {
23. using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
24. { 25. swEncrypt.Write(plainText); 26. } 27. encrypted = msEncrypt.ToArray(); 28. } 29. } 30. } 31. return encrypted; 32. }
56
menggunakan parameter plaintext. sebelum proses enkripsi terlebih dahulu dilakukan pengecekan terhadap masukan yang dibutuhkan dalam proses enkripsi pesan, diantaranya plaintext (pesan) dan kunci enkripsi pesan. Proses enkripsi dapat dilakukan jika plaintext dan kunci enkripsi pesan tidak bernilai null. Baris 13 – 32 menunjukkan proses enkripsi pesan dengan CryptoStream pada C#, adapun hasil dari enkripsi pesan adalah Byte cipher yang ditampung pada variabel Array.
5.2.6 Membuat citra hasil
Proses ini dilakukam dengan menyalin Byte citra asli dan pesan ke memori buffer, Gambar 5.6 menujukkan proses menyalin Byte citra asli dan pesan,
Gambar 5.6 Citra tanpa padding
private static Bitmap MakeNewUsedBitmap(ref Bitmap source, bool
isValidBitmap) 1. {
2. if (source == null)
3. return null;
4. int newWidth = source.Width;
5. int newHeight = source.Height;
6. if (isValidBitmap == false)
7. newWidth = source.Width - 1;
8. Bitmap result = new Bitmap(newWidth, newHeight, source.PixelFormat);
9. BitmapData bmpDataSrc = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, source.PixelFormat);
10. BitmapData bmpDataDst = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, result.PixelFormat);
11. int stride = bmpDataSrc.Stride;
12. int height = source.Height;
13. int width = source.Width;
14. unsafe
15. {
16. for (int i = 0; i < height; i++)
17. {
18. byte* pSrc = (byte*)(bmpDataSrc.Scan0 + i * bmpDataSrc.Stride); 19. byte* pDst = (byte*)(bmpDataDst.Scan0 + i * bmpDataDst.Stride);
20. for (int j = 0; j < stride; j++)
21. { 22. pDst[j] = pSrc[j]; 23. } 24. }} 25. source.UnlockBits(bmpDataSrc); 26. result.UnlockBits(bmpDataDst); 27. return result; 28. }
57
Method MakeNewUsedBitmap pada Gambar 5.6 memiliki 2 parameter yaitu Bitmap untuk Byte citra asli dan Boolean untuk mengecek status dari citra bitmap. Proses dari method ini menghasilkan bitmap. Selanjutnya Gambar 5.6 Baris 1 - 10 berfungsi untuk menghasilkan citra hasil dengan cara menyalin nilai Byte citra asli ke citra hasil sesuai dengan indeks baris dan indeks kolom, proses ini dapat dikerjakan dengan kondisi nilai Byte citra ≠ null. Selanjutnya untuk kondisi Gambar 5.1 maka 1 piksel width citra yang terakhir akan dijadikan sebagai lokasi untuk menyisipkan pesan, Proses penyalinan Byte citra asli ke citra hasil ditunjukkan pada Gambar 5.6 baris 11 – 28.
5.2.7 Penyisipan pesan
Berdasarkan hasil yang diperoleh pada Gambar 5.3 jika kondisi terpenuhi maka penyisipan pesan dapat dilakukan per Byte untuk setiap lokasi penyisipan. Pesan yang disisipkan pertama adalah header pesan sebesar 5 Byte, dengan dialokasi 3 Byte sebagai penanda pesan dan 2 Byte sebagai informasi panjang pesan. Selanjutnya penyisipan pesan pada lokasi penyisipan pesan/padding, pesan yang disisipkan berupa Byte cipher. Penyisipan pesan dilakukan sebesar panjang Byte pesan pada padding. Untuk citra dengan ukuran width modulo 4 = 0 Byte pesan disisipkan pada piksel terakhir per width sebesar 3 Byte sebesar panjang pesan, Gambar 5.7 baris ke – 14 menunjukkan kondisi penyisipan header pesan pada citra bitmap.
Berdasarkan Gambar 5.7 pada baris 1 – 6 berfungsi untuk mengambil nilai String penanda pesan untuk dikonversi ke Byte, selanjutnya mengambil panjang pesan kemudian dikonversi ke String biner untuk disimpan divariabel Array, bit – bit pada variabel Array selanjutnya dikonversi ke Byte, berikutnya nilai untuk Byte penanda pesan dan Byte panjang pesan disimpan di variabel Array. Selanjutnya Gambar 5.7 baris 7 – 17 adalah proses penyisipan header pesan, lokasi yang dibutuhkan untuk menyisipkan pesan sebesar indeks pada variabel Array yang menyimpan header pesan. Penyisipan header pesan akan dikerjakan sepanjang kondisi pengecekan indexByteCounter <
58
TotalSteganoHeaderSize, kondisi ini ditunjukkan pada Gambar 5.7 baris ke – 14, sedangkan lokasi untuk menyisipkan Byte pesan yang sedang dikerjkan ditunjukkan pada Gambar 5.7 baris ke – 16.
Tabel 5.6 Penyisipan byte pesan
Gambar 5.7 Penyisipan pesan
void GetBytesBitmapStegano() 1. {
2. GetBitmapData(); 3. IsSuccess = false;
4. if (BytesResultBitmap == null) {
5. AppMessageBox.ErrorMessage("Format image salah."); 6. return; }
7. if (BytesMessage.Length > MaxBytesStegano) 8. {
9. AppMessageBox.ErrorMessage(ResourceMessage.PesanTerlaluPanjang); 10. return; }
11. int messageLength = BytesMessage.Length;
12. byte[] bytesHeader = GetBytesHeader(SteganoHeaderText);
13. string stringMessageLength = Convert.ToString(messageLength,
2).PadLeft(16, '0');
14. byte[] bytesMessageLength =
General.GetByteArrayFromBinaryString(stringMessageLength); 15. byte[] bytesCompleteHeader = new byte[TotalSteganoHeaderSize]; 16. Buffer.BlockCopy(bytesHeader, 0, bytesCompleteHeader, 0,
SteganoHeaderText.Length);
17. Buffer.BlockCopy(bytesMessageLength, 0, bytesCompleteHeader, SteganoHeaderText.Length, bytesMessageLength.Length);
18. int indexByteMessage = 0; 19. int indexByteCounter = 0; 20. bool alreadyDone = false;
21. for (int i = 0; i < _heightImage; i++) {
22. for (int j = _widthImage * 3; j < _widthByte; j++) {
23. if (indexByteCounter < TotalSteganoHeaderSize) {
24. BytesResultBitmap[BitmapHeaderSize + (_widthByte * i) + j] = bytesCompleteHeader[indexByteCounter];
25. }
26. else if (indexByteMessage < BytesMessage.Length){
27. BytesResultBitmap[BitmapHeaderSize + (_widthByte * i) + j] = BytesMessage[indexByteMessage]; 28. indexByteMessage++; 29. } 30. if (indexByteMessage == BytesMessage.Length) 31. { 32. alreadyDone = true; 33. IsSuccess = true; 34. break; 35. } 36. indexByteCounter++; 37. } 38. if (alreadyDone){ 39. break; 40. } 41. } 42. BytesMessage = null; 43. }
59
Selanjutnya Gambar 5.7 baris 1 – 10 berfungsi untuk mengecek sebelum dilakukan penyisipan pesan, pengecekan dengan cara membandingkan Byte citra yang menjadi lokasi penyisipan pesan terhadap panjang pesan yang akan disisipkan. Selanjutnya baris 11 – 17 untuk mengambil data stegano yang akan disisipkan pada citra. Berikutnya baris 18 – 24 berfungsi untuk menyisipkan pesan, jika kondisi pada Gambar 5.7 baris ke - 22 belum terpenuhi maka penyisipan pesan pada citra bitmap dikerjakan. Proses penyisipan Byte pesan yang sedang dikerjakan ditunjukkan pada Gambar 5.7 baris ke – 27. Setiap menyisipkan header pesan dan Byte pesan nilai counter di-increment 1. Selanjutnya pada Gambar 5.7 baris 30 – 43 berfungsi untuk pengecekan kondisi penyisipan yang telah dikerjakan, penyisipan Byte pesan akan selesai dikerjakan jika nilai indeks penyisipan pesan telah sama dengan ukuran Byte pesan (indexByteMessage == BytesMessage.Length), kondisi ini ditunjukkan pada Gambar 5.7 barism– 30. Selama kondisi belum terpenuhi proses penyisipan Byte pesan akan dikerjakan. Proses increment indeks Byte pesan tunjukkan pada Gambar 5.7 baris ke - 36.
5.2.8 Proses stegano
Setelah proses Gambar 5.7 dikerjakan berikutnya dilakukan pengecekan untuk mengetahui keberhasilan penyisipan pesan pada citra hasil. Gambar 5.8 menunjukkan proses pengecekan status menyisipkan pesan,
Gambar 5.8 Proses stegano
public void DoStegano()
1. {
2. GetBytesBitmapStegano();
3. if (IsSuccess)
4. {
5. IsSuccess = false;
6. if (BytesResultBitmap != null && BytesResultBitmap.Length > 0)
7. {
8. IsSuccess = true;
9. }
10. }
60
Proses stegano dinyatakan berhasil setelah Byte citra, header pesan dan Byte pesan disalin atau tidak bernilai null dan panjang Byte citra hasil > 0, proses pengecekan keberhasilan proses stegano ditunjukkan pada Gambar 5.8 baris ke – 7, jika kondisi terpenuhi maka proses penyisipan pesan pada citra berhasil.
Gambar 5.8 baris 1 – 2 pada method DoStegano terdapat method GetBytesBitmapStegano merupakan method untuk menyalin Byte citra asli dan menyisipkan pesan ke citra hasil, hasil dari proses method ini selanjutnya dicek dengan kondisi citra yang dihasilkan tidak bernilai null dan panjang Byte citra hasil > 0.