August 28, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Steganography 101 using Java

  • September 6, 2005
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming, Notes # 732


Preface

What in the world is steganography?

This lesson is about steganography, (hidden writing) which should not be confused with stenography (shorthand).

According to Wikipedia,

Steganography is the art and science of writing hidden messages in such a way that no one apart from the intended recipient knows of the existence of the message; this is in contrast to cryptography, where the existence of the message is clear, but the meaning is obscured. The name comes from Johannes Trithemius's Steganographia: a treatise on cryptography and steganography disguised as a book on black magic, and is Greek for "hidden writing."

Steganography in spam

Because my email address is plastered all over the Internet, I receive an average of about 10,000 spam messages each month.  I have recently been receiving spam messages where the names and the prices of certain drugs are made to appear on the screen using steganographic techniques in conjunction with the rendering of HTML text.  In particular, the images of the letters and numbers that appear on the screen are constructed by carefully arranging the positions of very large numbers of very small characters.

(If you view the raw HTML source text, you see that the characters spell out random words and sentences and the name of the drugs don't appear anywhere in the text.)

In this case, the intended recipient of the secret content (names and prices of drugs) is the recipient of the email message.  The intent of the spammer is to disguise that content in such a way that it cannot be detected and flagged by spam filtering programs.  In other words, the intent of the spammer is to prevent the spam filtering software know of the existence of the spam message.

A cover message

Once again, according to Wikipedia,

Generally a steganographic message will appear to be something else, like a shopping list, an article, a picture, or some other "cover" message.

In this case, the cover message is an email message containing a seemingly innocuous page of raw HTML text.  The secret steganographic message is embedded in the HTML text by adjusting the placement and the size of characters that make up the innocuous raw HTML text.  It is the rendering of the HTML text by the browser that causes the secret content to appear on the screen when the email message is opened by the intended recipient.

Successful so far

Up until now, this has proven to be a successful steganographic technique for use in distributing spam and getting around my spam filtering software.  At this point in time, my spam filtering software is incapable of detecting the hidden content and categorizing the message as spam on the basis of that content.  This success will probably be short lived, however.  A new rule could be incorporated into the spam filter to the effect that an email message containing HTML text that is rendered in a font that is too small to be readable by a human observer is probably spam.

Steganography versus cryptography

As mentioned above, cryptography is often used in situations where the existence of the message is clear, but the meaning of the message is obscured.  In particular, the sender transforms the message into a form that (hopefully) only the intended recipient of the message can decrypt and read.

Steganography is often used in situations where the actual existence of the message needs to be obscured.

Steganography and cryptography can be combined

The message can also be encrypted before it is hidden inside a cover message.  This provides a double layer of protection.  To begin with, encryption may make the existence of the message even more difficult to detect, due to the fact that some encryption techniques cause the patterns of the characters in the encrypted version to be more random than in the original version.  In addition, even if the existence of the encrypted message is detected, it is unlikely that an evesdropper will be able to read the message.

A practical comparison of steganography and cryptography

Let's take a look at a couple of examples that illustrate the use of steganography and cryptography both separately and together.

Purchasing some books online

As our first example, we will assume that you purchase some Java programming books from an online vendor.  When time comes to pay for the books by supplying your credit card number, you check to make certain that your browser has switched into the secure mode.  Even though it is no secret that you are sending your credit card number to pay for the books, it is your hope that the transmission is encrypted by the browser in such a way that only the online vendor can decrypt the message and read your credit card number.  In this case, there is no need for steganography because there is no need to hide the existence of the message.

A romantic tryst

As our second example, we will assume that you are sending a message to Alice (your best friend's wife) to arrange a romantic tryst.  In this case, you would probably prefer that neither your wife nor your best friend know of the existence of the message.

 (If they learn of the secret message, they may not continue to be your wife and your best friend for very much longer.)

Consequently it would probably be wise for you to use some form of steganography to hide the existence of the secret message.  It also wouldn't hurt to encrypt the message using Alice's public key.  That way, even if the existence of the message is detected, it wouldn't be possible for your wife or your best friend to read it.

(That may increase your chances of survival even if the existence of the message is detected.)

Embed the message in an image

There are several different forms of steganography that you could use to hide the existence of the message.  One way would be to send your latest set of digital vacation photos to Alice and her husband, and to embed the message in one of the photos.

Having planned for and having written the necessary software for just such a situation in advance, Alice could extract and read your message.  As far as Alice's husband is concerned, the only thing being transmitted would probably be a set of (probably boring) vacation photos.  The existence of a secret message would certainly not be obvious in viewing the photos.

Alice could embed her answer in a digital photo of her championship Weimaraner (showing the blue ribbon that her dog won at the most recent show).  She could send that picture to you and your wife.  You could extract and read the message.  Your wife could say courteous things about the dog.

Detection is unlikely

As you will see later in this lesson, it is extremely unlikely that either your wife or Alice's husband would be able to detect the existence of the secret messages simply from viewing the photos.  They would probably need some other clues, (such as otherwise suspicious behavior on the part of you or Alice), in order to even suspect that a secret message is embedded in the photo.

You and Alice could continue to secretly communicate in this manner for as long as you could come up with reasons to exchanges digital photos.

(That approach might get stale after a while and you would need to come up with some other form of steganography to continue the dialog.)

Embedding a message in an image

In this lesson, I will show you how to embed a secret message in an image and how to extract the message from the image in order to convert it back into a readable form.

A merger

This lesson represents a merger between my series of lessons dealing with the manipulation of pixels in images and my series dealing with cryptography and security.  You may find it useful to review the lessons in the following list while studying this lesson:

This lesson builds upon those earlier lessons.  In particular, you will need to understand the code in the lesson entitled Processing Image Pixels using Java, Getting Started before the code in this lesson will make much sense.

You will need a driver program

The lesson entitled Processing Image Pixels Using Java, Controlling Contrast and Brightness provided and explained a program named ImgMod02a.  This program makes it easy to:

  • Manipulate and modify the pixels that belong to an image.
  • Display the processed image along with the original image for a visual comparison of the two.

ImgMod02a serves as a driver that controls the execution of a second program that actually processes the pixels.

The programs that I will explain in this lesson run under the control of ImgMod02a.  In order to compile and run the programs that I will provide in this lesson, you will need to go to the lessons entitled Processing Image Pixels Using Java, Controlling Contrast and Brightness and Processing Image Pixels using Java, Getting Started to get copies of the program named ImgMod02a and the interface named ImgIntfc02.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different figures and listings while you are reading about them.

Display format

The output shown in Figure 1 was produced by the driver program named ImgMod02a and the image-processing program named ImgMod28.  The program named ImgMod02a was explained in an earlier lesson.  I will explain the program named ImgMod28 (along with another program named ImgMod28a) in this lesson.


Figure 1

As in all of the graphic output produced by the driver program named ImgMod02a, the original image is shown at the top and the processed image is shown at the bottom.

Background Information

The earlier lesson entitled Processing Image Pixels using Java, Getting Started provided a great deal of background information as to how images are constructed, stored, transported, and rendered.  I won't repeat that material here, but will simply refer you to the earlier lesson.

That earlier lesson introduced and explained the concept of a pixel.  In addition, the lesson provided a brief discussion of image files, and indicated that the program named ImgMod02a is compatible with gif files, jpg files, and possibly some other file formats as well.

However, with the exception of the discussion on file compression errors later in this lesson, the lessons in this series are not particularly concerned with file formats.  Rather, the lessons are concerned with what to do with the pixels after they have been extracted from an image file.  Therefore, there is very little discussion about file formats in this series.

A three-dimensional array of pixel data as type int

The driver program named ImgMod02a:

  • Extracts the pixels from an image file.
  • Converts the pixel data to type int.
  • Stores the pixel data in a three-dimensional array of type int that is well suited for processing.
  • Passes the three-dimensional array object's reference to an image-processing program.
  • Receives a reference to a three-dimensional array object containing processed pixel data from the image-processing program.
  • Displays the original image and the processed image in a stacked display as shown in Figure 1.

The manner in which that is accomplished was explained in the earlier lesson entitled Processing Image Pixels using Java, Getting Started.

Will concentrate on the three-dimensional array of type int

The lessons in this series will show you how to write image-processing programs that implement a variety of image-processing algorithms.  The image-processing programs will receive raw pixel data in the form of a three-dimensional array of type int, and will return processed pixel data in the form of a three-dimensional array of type int.

The two programs that I will explain in this lesson will embed secret text messages in the pixels of an image before returning the processed image for display.  A future program will explain how to embed a secret image in another image as a more technically advanced form of steganography.

A grid of colored pixels

Each three-dimensional array object represents one image consisting of a grid of colored pixels.  The pixels in the grid are arranged in rows and columns when they are rendered.  One of the dimensions of the array represents rows.  A second dimension represents columns.  The third dimension represents the color (and transparency) of the pixels.

Fundamentals

Once again, I will refer you to the earlier lesson entitled Processing Image Pixels using Java, Getting Started to learn:

  • How the primary colors of red, green, and blue and the transparency of a pixel are represented by four unsigned 8-bit bytes of data.
  • How specific colors are created by mixing different amounts of red, green, and blue.
  • How the range of each primary color and the range of transparency extends from 0 to 255.
  • How black, white, and the colors in between are created.
  • How the overall color of each individual pixel is determined by the values stored in the three color bytes for that pixel, as modified by the transparency byte.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

Preview

Five programs and one interface

The image-processing programs that I will discuss in this lesson require the driver program named ImgMod02a and the interface named ImgIntfc02 for compilation and execution.  I provided and explained that material in the earlier lessons entitled Processing Image Pixels Using Java, Controlling Contrast and Brightness and Processing Image Pixels using Java, Getting Started.

I will present and explain two new Java programs named ImgMod28 and ImgMod28a in this lesson.  These programs, when run under control of the program named ImgMod02a, will produce outputs similar to those shown in Figure 1.

(The results will be different if you use a different image file.)

The processed image matches the original image

In previous lessons in this series, the processed image was usually different from the original image, and it was this difference that was significant with respect to the topic of the lesson.  The significant thing about Figure 1, Figure 3, Figure 6, and Figure 7 is that the processed image at the bottom looks just like the original image at the top.

This is significant because in Figure 1, Figure 3, Figure 6, and Figure 7 there is a text message hidden in the bottom image.  A short text message is hidden in the bottom image of Figure 1 and Figure 3.  A very long text message is hidden in the bottom image of Figure 6 and Figure 7.  In all four cases, the existence of the message cannot be detected simply by viewing the image.

I will show you how to write the Java code to hide the message in the image.  I will also show you how to extract and display the message that is hidden in the image.

The processImg method

The programs named ImgMod28 and ImgMod28a, (and all image-processing programs that are capable of being driven by ImgMod02a), must implement the interface named ImgIntfc02.  That interface declares a single method named processImg, which must be defined by all implementing classes.

When the user runs the program named ImgMod02a, that program instantiates an object of the image-processing program class and invokes the processImg method on that object.

A three-dimensional array containing the pixel data for the image is passed to the processImg method.  The processImg method returns a three-dimensional array containing the pixel data for a processed version of the original image.

A before and after display

When the processImg method returns, the driver program named ImgMod02a causes the original image and the processed image to be displayed in a frame with the original image above the processed image as shown in Figure 1.

The image file

The image file can be a gif file or a jpg file.  Other file types may be compatible as well.  If the program is unable to load the image file within ten seconds, it will abort with an error message.

(You should be able to right-click on the image in Figure 8 to download and save the image locally.  Then you should be able to replicate the output produced in Figure 1 by running the program named ImgMod28 and specifying that image file as the input.)

The Replot button

A Replot button appears at the bottom of the frame in Figure 1. If the user clicks the Replot button, the processImg method is rerun, the image is reprocessed, and the new version of the processed image replaces the old version in the display.

This feature is of no significance with the program named ImgMod28 because there is no change between successive runs of the processImg method.  This feature could be significant for the program named ImgMod28a, because a new message containing random characters is generated and hidden each time the processImg method is run.  Thus, it is possible that the output image could be different from one run to the next for the program named ImgMod28a.

Discussion and Sample Code

The program named ImgMod28

This program, which is designed to be driven by the program named ImgMod02a, illustrates one form of steganography by:

  • Hiding a text message in an image.
  • Displaying the original message.
  • Extracting and displaying the hidden message for comparison with the original.
  • Displaying the modified image below the original image for a visual comparison of the two images.

Running the program

Enter the following on the command line to run this program:

java ImgMod02a ImgMod28 ImagePathAndFileName

Hiding the message

This program hides the message in the image by doing the following:

  • Reducing the message to the 64-character set beginning with the space character in the ASCII table.  Each character can then be represented by six bits.
  • Decomposing each six-bit character into three groups of two bits each.
  • Replacing the two least-significant bits (LSB) in the red, green, and blue values for a given pixel by the three groups of two bits.  Thus, each pixel can carry one six-bit character.

Camouflage

In an attempt to make it difficult for an evesdropper to detect that a message is embedded in the image, the message is not embedded at the beginning of the image.  Rather the image is embedded beginning at a predetermined pixel location somewhere internal to the image.

More camouflage

In an attempt to make it even more difficult for an evesdropper to detect that a message is embedded in the image, message values are not embedded into adjacent pixels.  Rather, a space consisting of 0, 1, 2, or 3 pixels is allowed between modified pixels.  Furthermore, the space that is allowed between any two modified pixels is probably random because it is based on the value of two bits from the previous message character.

May or may not be helpful

While this feature probably makes the existence of the hidden message more difficult to detect, it also makes the overall process more vulnerable to problems resulting from data transmission errors.  Once a data error causes the reconstruction process to lose synchronization, it is probably not possible for the reconstruction process to recover.  Therefore, this feature may, or may not be advantageous depending on the likelihood of transmission errors affecting the low order bits in the pixel color values. 

This feature is easy to disable (see the program named ImgMod28a for a version of the program in which this feature has been disabled).  This feature should be disabled if there is any possibility of transmission errors affecting the low order bits in the pixel color values.

The character set

The program supports a message character set consisting of 64 characters beginning with the space character in the ASCII table.  This set includes the space character, the numeric characters, the upper-case alphabetic characters, and most of the punctuation and special characters.

By using this format, each character can be represented in six bits, and each modified pixel can carry one message character.

The end-of-message character

The program uses an underscore character to indicate the end of the message.  Therefore, the underscore character cannot be used elsewhere within the body of the message.  Thus, the effective number of allowable characters is reduced from 64 to 63 by the use of the underscore character to flag the end of the message.

An output example

This program produces the output shown in Figure 2 when run with an image file named imgmod10.jpg.  (This is the image shown in Figure 1.  You can download this image from Figure 8 later in the lesson.)

Note that line breaks were manually inserted in Figure 2 to force this material to fit into this narrow publication format.

Processing program: ImgMod28
Image file: imgmod10.jpg
Width = 324
Height = 330
Original message:
Four score and seven years ago our fathers brough
t forth on this continent a new nation, conceived
 in liberty and dedicated to the proposition that
 all men are created equal.
Msg Length = 175
Insertion point = 5000
Reconstructed message:
FOUR SCORE AND SEVEN YEARS AGO OUR FATHERS BROUGH
T FORTH ON THIS CONTINENT A NEW NATION, CONCEIVED
 IN LIBERTY AND DEDICATED TO THE PROPOSITION THAT
 ALL MEN ARE CREATED EQUAL._
Figure 2

Conversion to all upper case

The text of the original message that was converted to a 64-character set and embedded in the image is shown near the middle of Figure 2.  That text consists of 175 characters beginning with the word Four and ending with EQUAL._

As you can see, the text was converted from mixed case to all upper case.  This conversion was a part of converting the message to the set of only 64 characters.  This reduction in the size of the character set is not a requirement for embedding a message in an image.  It simply makes it easier because the six bits that comprise a character fit nicely into the two LSB of the three color values.  The use of 8 or 16-bit characters would require additional control logic in the character embedding and character extraction process but would be entirely feasible.

A fairly short message

The message used to test the program in this case was fairly short (175 characters).  I did this on purpose to make it practical to display and to compare the original message and the reconstructed message in a reasonable amount of screen space.

A much longer message

An image of this size could be used to hide a much longer message.  For example, if the pixel-skipping feature remains enabled and if it is assumed that on the average one character is embedded in every three pixels, this image would accommodate a message with a length in excess of 35,600 characters.  As you will see later, if the pixel-skipping feature is disabled and one character is embedded in every pixel, this image would accommodate a message with a length in of 106,920 characters.

As you can see, the message-hiding capacity of this approach is substantial.

Not visible in the modified image

If you scroll back to Figure 1 and compare the modified image on the bottom with the original image at the top, you will see that there is no visual indication that the message is embedded in the image.

What about other images?

One might hypothesize that the existence of the embedded message is not visible in the modified image for the following reasons:

  • The image contains a lot of detail with many different shades of several different colors.  Thus, a slight change in the shade of a given pixel would be difficult to see.
  • The number of pixels in the image is much greater than the number of characters in the message.  Therefore, only 175 of 106,920 pixels were actually modified, and those were not adjacent pixels.

One might further hypothesize that if either of these conditions is not met, the existence of the message will become visible.

The next example will test the hypothesis that the message would be visible in an image with less color variation.  The program named ImgMod028a will test the hypothesis that the message would become visible in the image if the number of characters is a large percentage of the number of pixels.

The image named imgmod11.jpg

Figure 3 shows the result of using the same program to embed the same message in a different image named imgmod11.jpg.


Figure 3

Once again, the original image is shown at the top, and the image containing the embedded message is shown at the bottom.  (The two images are separated by a very narrow yellow horizontal line in Figure 3.)

No visual indication of a message

There is no visual indication that the message was embedded in the bottom image in spite of the fact that there was virtually no color detail in this image.  This image contains large blocks of only eight colors consisting of:

  • Black and white
  • The primary colors red, green, and blue
  • The secondary colors aqua, yellow, and fuchsia 

The text output

Just to prove that the lower image in Figure 3 does contain a message, the text output produced by this program for this image is shown in Figure 4.

Processing program: ImgMod28
Image file: imgmod11.jpg
Width = 480
Height = 120
Raw message:
Four score and seven years ago our fathers brough
t forth on this continent a new nation, conceived
 in liberty and dedicated to the proposition that
 all men are created equal.
Msg Length = 175
Insertion point = 5000
Extracted message:
FOUR SCORE AND SEVEN YEARS AGO OUR FATHERS BROUGH
T FORTH ON THIS CONTINENT A NEW NATION, CONCEIVED
 IN LIBERTY AND DEDICATED TO THE PROPOSITION THAT
 ALL MEN ARE CREATED EQUAL._
Figure 4

The only differences in the text output in Figure 4 and the text output in Figure 2 has to do with the name and the size of the image file.  The same message was embedded in the modified image in both cases and was later extracted and displayed.

Conclusion

The conclusion is that the human eye is incapable of discerning changes that take place in the two LSB of the color values when an image that uses three 8-bit color values and one 8-bit alpha value is displayed on a computer screen.

(At least, that is the case for my eyes and my computer screen.)

This suggests that it might be possible to use three and possibly four of the least significant bits to embed the message provided that the image was carefully chosen to contain lots of color and lots of fine detail.  However, I am unable to test that hypothesis without making major modifications to the program.

(That sounds like a good homework assignment.)

Enough talk, let's see some code

I will break this program down and discuss it in fragments.  You can view the entire program named ImgMod28 in Listing 10 near the end of the lesson.

The program named ImgMod28

As shown in Listing 1, the class named ImgMod28 begins just like most of my classes that implement the interface named ImgIntfc02.  I have explained this code in several previous lessons and won't repeat that explanation here.  Rather, I will simply allow the comments to speak for themselves.

class ImgMod28 implements ImgIntfc02{
  //This method must be defined to implement
  // the interface named ImgIntfc02.
  public int[][][] processImg(
                             int[][][] threeDPix,
                             int imgRows,
                             int imgCols){
    //Make a working copy of the 3D array to
    // avoid making permanent changes to the
    // image data.
    int[][][] temp3D =
                    new int[imgRows][imgCols][4];
    for(int row = 0;row < imgRows;row++){
      for(int col = 0;col < imgCols;col++){
        temp3D[row][col][0] =
                          threeDPix[row][col][0];
        temp3D[row][col][1] =
                          threeDPix[row][col][1];
        temp3D[row][col][2] =
                          threeDPix[row][col][2];
        temp3D[row][col][3] =
                          threeDPix[row][col][3];
      }//end inner loop
    }//end outer loop
    System.out.println("Width = " + imgCols);
    System.out.println("Height = " + imgRows);
Listing 1

The original message

Listing 2 defines the message that will be embedded in the image.  This message is created as a String object using the 16-bit Unicode character set that is standard for Java.  The message is terminated with an underscore character.

(The use of a termination character is necessary so that the code that later extracts the message from the image will know when it has reached the end of the message.)

    String msg =
     "Four score and seven years ago our fathers"
     + " brought forth on this continent a new"
     + " nation, conceived in liberty and"
     + " dedicated to the proposition that all"
     + " men are created equal.";
     System.out.println(
                    "Original message:n" + msg);
     
     //Terminate the message with an underscore.
     msg = msg + "_";
Listing 2

Listing 2 also displays the original message as shown in Figure 2 and Figure 4.

Convert to six-bit characters

The next step is to convert the Unicode characters to a set of 64 characters, which can be represented as six bits per character.  This is accomplished in Listing 3.

    //Convert the message to 64-char six-bit
    // data format. This includes the space,
    // the numbers, the upper-case characters,
    // and most of the punctuation characters
    // and other special characters.
    //Begin by converting to all upper-case
    // characters.
    msg = msg.toUpperCase();
    //Create an object to receive the modified
    // version of the message.
    StringBuffer sixBitMsg = new StringBuffer();
    //Subtract a value of 32 causing all of the
    // modified character values to fall between
    // 0 and 63.  This bias value of 32 will be
    // added back later when the message is
    // reconstructed.
    char temp;
    for(int cnt = 0;cnt < msg.length();cnt++){
      temp = (char)(msg.charAt(cnt) - 32);
      sixBitMsg.append(temp);
    }//end for loop
    //Replace the original Unicode message with
    // the 6-bit message.  This is the message
    // that will be hidden in the image.
    msg = new String(sixBitMsg);
Listing 3

Listing 3 is rather long, but the code in Listing 3 is straightforward and should not require a detailed explanation.  Therefore, once again, I will let the comments speak for themselves.

Embed the message in the image

The next step is to embed the message in the image.  The overall process consists of two steps:

  • Break each six-bit character down into three pairs of bits
  • Substitute those three pairs of bits for the two least-significant bits (LSB) of the red, green, and blue color values in selected pixels in the image

Extract and save the pairs of bits

The process begins by extracting each pair of bits from each six-bit character and storing the two bits in the two LSB of a byte in an array of bytes.  This makes it easier to access the pairs of bits later for use in modifying the pixel values.

Start the process by executing the first statement in Listing 4 to convert the message from a String of Unicode characters into an array of bytes.

(Note that this process preserves only the 8 LSB of each 16-bit Unicode character.)

    byte[] msgBytes = msg.getBytes();
    System.out.println(
              "Msg Length = " + msgBytes.length);
    //Create an array to store the bytes
    // containing the pairs of bits.
    byte[] twoBitBytes = 
                   new byte[3 * msgBytes.length];
    
    //Shift and mask the bytes in the array
    // containing the message bytes to get and
    // save the pairs of bits.
    int twoBitByteCnt = 0;
    for(byte element:msgBytes){
      twoBitBytes[twoBitByteCnt++] 
                        = (byte)(element & 0x03);
      twoBitBytes[twoBitByteCnt++] 
                 = (byte)((element >> 2) & 0x03);
      twoBitBytes[twoBitByteCnt++] 
                 = (byte)((element >> 4) & 0x03);
    }//end for-each
Listing 4

Then extract the bit pairs from the characters and store them in the array referred to by twoBitBytes.  This is all accomplished in Listing 4.

The code in Listing 4 is relatively straightforward, particularly if you are familiar with bit-level programming in Java.

Embed the bits in the pixels

The next step is to embed the pairs of message bits into the pixel color values beginning at a predetermined pixel location in the image.

Two bits from a single 6-bit message character are embedded into the two LSB of the red, green, and blue pixel values belonging to a single image.

After a pixel has been modified, the values of the two bits that were embedded into the blue pixel are used to determine the number of pixels to skip before modifying another pixel.  The number of pixels skipped will be 0, 1, 2, or 3.  This prevents adjacent pixels from being modified about three-fourths of the time.

This is accomplished by the code in Listing 5.

    //Specify the predefined insertion point
    int insertionPoint = 5000;
    System.out.println(
          "Insertion point = " + insertionPoint);
    //Initialize the number of pixels to skip.
    int skipValue = 0;
    //Use a nested loop to operate on each pixel
    // and to embed message bits into the red,
    // green, and blue values of selected pixels.
    twoBitByteCnt = 0;//Re-use this variable
    for(int row = 0;row < imgRows;row++){
      for(int col = 0;col < imgCols;col++){
        //Embed 6-bit message characters
        // beginning at a predefined pixel
        // location in the image and skipping
        // pixels on the basis of the value of
        // two bits in the message character.
        if((row * col > insertionPoint) 
          && (twoBitByteCnt < twoBitBytes.length)
          && skipValue-- == 0){
          //Replace the two lsb of the red,
          // green, and blue pixel values with
          // two bits from the six-bit message
          // character.
          temp3D[row][col][1] = 
                (temp3D[row][col][1] & 0xFC)
                  | twoBitBytes[twoBitByteCnt++];
          temp3D[row][col][2] = 
                (temp3D[row][col][2] & 0xFC)
                  | twoBitBytes[twoBitByteCnt++];
          temp3D[row][col][3] = 
                (temp3D[row][col][3] & 0xFC)
                  | twoBitBytes[twoBitByteCnt++];
          //Determine number of pixels to skip
          // before modifying another pixel.
          skipValue = 
                  twoBitBytes[twoBitByteCnt - 1];
        }//end if
      }//end for loop on col
    }//end for loop on row
Listing 5

Once again, although the code in Listing 5 is rather long, it is completely straightforward if you are familiar with bit-level programming in Java.

Once the code in Listing 5 has been executed, the message has been successfully embedded in the image.

Extract the message from the image

Up to this point, the program shows how to modify an image to include a hidden message.  The remainder of the program shows how to extract the message data from the image, reconstruct the message, and display the message.  (Note that this code requires prior knowledge of the insertion point value.)

An array to contain the two-bit message data

Begin by creating an array object large enough to contain one byte for every two bits of message data that can be extracted from every pixel between the insertion point and the end of the message.  This is shown in Listing 6.

    byte[] extractedTwoBitBytes = 
                     new byte[(imgRows * imgCols 
                          - insertionPoint) * 3];
Listing 6

A pair of nested for loops

The next step is to use a pair of nested for loops to process every pixel, extracting the two message bits from the color values for those pixels that contain message data.

(Recall that as a result of the pixel-skipping feature, only about one out of three or four pixels actually contain message data.)

The code in Listing 7 extracts and saves the pairs of message bits in the two LSB of the bytes in the byte array referred to by extractedTwoBitBytes.

    twoBitByteCnt = 0;
    skipValue = 0;
    //Process every pixel.
    for(int row = 0;row < imgRows;row++){
      for(int col = 0;col < imgCols;col++){
        if((row * col > insertionPoint) 
                          && (skipValue-- == 0)){
          //Extract and save the message
          // characters two bits at a time.
          extractedTwoBitBytes[twoBitByteCnt++]
            = (byte)(temp3D[row][col][1] & 0x03);
          extractedTwoBitBytes[twoBitByteCnt++] 
            = (byte)(temp3D[row][col][2] & 0x03);
          extractedTwoBitBytes[twoBitByteCnt++] 
            = (byte)(temp3D[row][col][3] & 0x03);
          //Determine number of pixels to skip
          // before extracting data for another
          // pixel.
          skipValue = extractedTwoBitBytes[
                              twoBitByteCnt - 1];
        }//end if
      }//end for loop on col
    }//end for loop on row
Listing 7

Two-bit message data has been extracted and saved

Once the code in Listing 7 finishes execution, all of the message data has been extracted from the image and saved in the byte array.  At this point, however, the bits for each six-bit character are spread across three bytes in the array, two data bits in each byte.  Those bits must be recombined in order to form a readable message.

Reconstruct the message

The code in Listing 8 uses bit-level programming to reconstruct each six-bit character into an eight-bit byte using the three pairs of bits contained in three consecutive bytes in the byte array referred to by extractedTwoBitBytes.

    //Reconstruct and display the 64-char version
    // of the message
    StringBuffer extractedMsg = 
                              new StringBuffer();
    byte[] sixBitBytes = 
         new byte[extractedTwoBitBytes.length/3];
    twoBitByteCnt = 0;
    for(byte element:sixBitBytes){
      //Shift and or the pairs of bits into the
      // 6-bit format.
      element = (byte)(extractedTwoBitBytes[
                               twoBitByteCnt++]);
      element = 
          (byte)(element | (extractedTwoBitBytes[
                         twoBitByteCnt++] << 2));
      element = 
          (byte)(element | (extractedTwoBitBytes[
                         twoBitByteCnt++] << 4));
      extractedMsg.append((char)(element + 32));
      //Stop processing when terminating
      // underscore character is encountered.
      if((char)(element + 32) == '_')break;
    }//end for-each
Listing 8

Add the bias value back in

In addition, the code in Listing 8 adds in a bias value of 32 to compensate for the fact that a value of 32 was subtracted in Listing 3 during the process of representing the 64 characters in six-bit data.  This causes the six bits that represent the 64 characters to be shifted back to their proper locations in an eight-bit byte.

Monitor for message-termination character

While reconstructing the message, the code in Listing 8 monitors for the occurrence of the underscore terminating character.  When it is encountered, the reconstruction process is terminated by breaking out of the processing loop.  Once this is done, the StringBuffer object referred to by extractedMsg contains the extracted message in 16-bit 64-character format.

Display the results

Listing 9 displays the extracted message on the standard output device as shown in Figure 2 and Figure 4.  Listing 9 also returns the modified image to be displayed as shown in Figure 1 and Figure 3.

    System.out.println("Extracted message:n"
                                 + extractedMsg);
    //Return the modified image for display.
    return temp3D;
  }//end processImg
Listing 9

What about a longer message?

It was hypothesized earlier that an embedded message might be visible in an image that contains very little detail.  That hypothesis was refuted by Figure 3 that shows a message embedded in an image containing only eight colors with no detail.  There was no visible indication that the image contains an embedded message.

It was also hypothesized earlier that a message embedded in an image might be visible if the number of characters in the message is a large percentage of the number of pixels in the image.  This hypothesis will be tested by the program named ImgMod28a.

The program named ImgMod28a

Because the program is very similar to the program named ImgMod28, I won't break it down and discuss the code in fragments.  You can view this program in its entirety in Listing 11 near the end of the lesson.

This program is a modified version of the program named ImgMod28.  The purpose of this program is to determine the extent to which randomly modifying the two LSB of every pixel in an image degrades the visual quality of the image.  In other words, if the two LSB in every color value in every pixel is modified by embedding a message, will be existence of the message be detectable simply by viewing the image?

Program overview

This program is designed to be driven by the program named ImgMod02a.  It illustrates the use of steganography for very long messages by:

  • Embedding a long text message in an image by modifying every pixel in the image to contain one character from the message.  (The message is composed of characters generated on the basis of a sequence of output values provided by a random number generator.  This is similar to what might be expected if the message had been encrypted.)
  • Displaying selected characters from throughout the original message.  (Because of the length of the message, it is not practical to display all of the characters.)
  • Extracting the message from the image and displaying the same set of selected characters from the extracted version for comparison with the original message.
  • Displaying the modified image below the original image for a visual comparison of the two images.  This is the point where visual detection of the message will occur if it does occur.

Running the program

You can run the program by entering the following at the command line:

java ImgMod02a ImgMod28a ImagePathAndFileName

Support for character set

As is the case with the earlier program named ImgMod28, this program supports a message character set consisting of 64 characters beginning with the space character in the ASCII table.  This character set includes the space character, the numeric characters, the upper-case alphabetic characters, and most of the punctuation and special characters.  By using this format, each modified pixel can carry one message character.

A smaller random character set is actually used

Note however that the random message used to test this program does not contain all of the allowable characters.  This is acceptable since the purpose of the program is to test for the ability to visually detect the message caused by replacing the two LSB in every color value for every pixel with a pair of randomly determined bits.

As before, the program uses an underscore character to indicate the end of the message.  Therefore, the underscore character cannot be used within the body of the message so the effective number of allowable characters is reduced from 64 to 63.

The text output

This program produced the output shown in Figure 5 when run with the image file named imgmod10.jpg.

(This is the same image file that was used to test the program named ImgMod28 earlier.)

Processing program: ImgMod28a
Image file: imgmod10.jpg
Width = 324
Height = 330
Characters from original message:
%1" 9:)<8;>';4$:!?,,*"'+4:6$-'?!2: (3$8 :729$&;%&
'(-724
Msg Length = 106920
Insertion point = 0
Characters from reconstructed message:
%1" 9:)<8;>';4$:!?,,*"'+4:6$-'?!2: (3$8 :729$&;%&
'(-724
Figure 5

Note that line breaks were manually inserted in Figure 5 to force this material to fit into this narrow publication format.  Note also that because the message that is embedded in the image is generated using a random number generator, the output will be different each time the program is run.

Reconstructed characters match original characters

The output shown in Figure 5 contains several interesting aspects.  To begin with, the characters that are displayed from the reconstructed message match the characters displayed from the original message proving that the message was actually embedded in the image and later extracted successfully.

Number of characters equal number of pixels

Next, a message character was embedded in every pixel beginning with the first pixel resulting in a message length of 106,920 characters.  By many standards, that would be a fairly long message.  (In raw HTML form, this tutorial lesson contains about 63,000 characters.)

Was the message visually detectable?

That brings us to the question of visual message detectability resulting from the embedding of one character in every pixel.  The input and output images for the starfish image are shown in Figure 6.  As near as I can tell, there was no visual degradation of the bottom image caused by embedding this message in this image.  There appear to be no visual clues in the output image that would cause someone to suspect that a very long message has been embedded in the image.


Figure 6

A more severe test case

An even more severe test is shown in Figure 7.  This figure shows the input and output images for the image named imgmod11.jpg that consists of only eight solid colors.  Once again, one message character was embedded in every pixel with no visual indication that the modified image contains a hidden message.


Figure 7

Conclusion reinforced

This reinforces the conclusion drawn earlier that the human eye is incapable of discerning changes that take place in the two LSB of the color values when an image that uses three 8-bit color values and one 8-bit alpha value is displayed on a computer screen.

Now for the bad news

Up to this point, all of the news has been good news.  If you want to send that secret message to Alice by embedding it in one of your digital vacation photos, you can probably do so without fear of being caught.

Avoid lossy compression schemes

However, you must be very careful about the handling of the image in order to make certain that data transmission errors won't cause changes to the two LSB of the color values, thus destroying your secret message.  In particular, you must avoid using lossy image compression schemes to store and transmit the image.  Here is some of what one author has to say about the JPEG file format:

"JPEG, ... is a lossy compression method. In other words, to save space it just throws away parts of an image."

Therefore, you probably don't want to send your secret message to Alice by embedding and sending it to her in a JPEG image.  (An image file with a jpg extension.)  If you do, the message may be badly garbled or completely impossible to read.

Why did I use JPEG image files?

At this point, you may have observed that the images that I used in this lesson were JPEG images, and you may have wondered why this wasn't a problem for me.  The reason is that although I read a JPEG image file as input, I didn't write the modified image back out into another JPEG file before extracting the message.  Instead, I extracted the message from the image while it was still in raw pixel form.  Had I written the image back out into a JPEG file, and then read the file and extracted the message, the results may have been much different.

What about GIF files?

Here is part of what that same author has to say about the GIF file format:

"GIF, ... is a lossless method of compression. ... when the program that creates a GIF squashes the original image down it takes care not to lose any data. ... they are limited to a palette of 256 colours or less. ... There is a 24-bit, license-free GIFalike called the PNG format, ..."

Therefore, it would probably be safe to embed your secret message to Alice in a GIF file.  Just be sure to convert to GIF format before embedding the message in the image.  Unless data errors crept in from some other source, Alice should be able to successfully extract and read the message.  However, your vacation photos may not look quite as good after you convert them to GIF files as they did when you first retrieved them from your digital camera.

What about PNG files?

Here is part of what another author has to say about the PNG file format:

"PNG file format provides ... for lossless image compression. It supports true color (PNG-24) and palletized (PNG-8) images ... PNG-24 is a useful image file format for high quality lossless image archiving, ..."

Therefore, the PNG file format might be an appropriate format for sending your secret messages to Alice embedded in your digital vacation photos.  Reportedly PNG provides high-quality color as well as lossless compression.

What about lossless JPEG?

Finally, here is what another author has to say about something called lossless JPEG.

"There's a great deal of confusion on this subject, ... there are several different compression methods all known as "JPEG".  The commonly used method is "baseline JPEG" ...  The same ISO standard also defines a very different method called "lossless JPEG".  ...  When I say "lossless", I mean mathematically lossless: a lossless compression algorithm is one that guarantees its decompressed output is bit-for-bit identical to the original input.  This is a much stronger claim than "visually indistinguishable from the original".  Baseline JPEG can reach visual indistinguishability for most photo-like images, but it can never be truly lossless.  Lossless JPEG is a completely different method that really is lossless.  ... it is now largely obsolete.  ... the new PNG standard outcompresses lossless JPEG on most images.)  ... It's worth repeating that cranking a regular JPEG implementation up to its maximum quality setting *does not* get you lossless storage; even at the highest possible quality setting, baseline JPEG is lossy because it is subject to roundoff errors in various calculations.  ..."

The bottom line

And that is probably more than you ever wanted to know about lossy and lossless image compression algorithms.

The bottom line is, if you are going to embed a message in an image, be sure to write your modified image into a file using a lossless compression algorithm.  Otherwise, your message will probably become garbled due to the data errors introduced by lossy compression.

Depending on the color quality that you need, the two best choices as of the date of this writing appear to be GIF for lower color quality and PNG for higher color quality.

(You can read more about the distribution of color in GIF and JPEG images in my earlier lesson entitled Processing Image Pixels Using Java: Controlling Contrast and Brightness.  That lesson uses histograms to illustrate and compare the distribution of color for those two image file formats.)





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel