javasteganography

Java steganography wrong output


I wanted to learn steganography in java and found a repo on github which seemed simple enough to learn from it: https://github.com/tigerlyb/Steganography-in-Java. I have a problem with the text embedding and extracting - For some reason some characters are incorrectly extracted. I can't figure out the mistake in code since it looks correct and the positions of incorrect characters seems random.

Here is the code from the repository i mentioned before:

// Steganography.java 

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Steganography {
    // embed secret information/TEXT into a "cover image"
        public static BufferedImage embedText(BufferedImage image, String text) {
            int bitMask = 0x00000001;   // define the mask bit used to get the digit
            int bit;                // define a integer number to represent the ASCII number of a character
            int x = 0;              // define the starting pixel x
            int y = 0;              // define the starting pixel y
            for(int i = 0; i < text.length(); i++) {            
                bit = (int) text.charAt(i);     // get the ASCII number of a character
                for(int j = 0; j < 8; j++) {
                    int flag = bit & bitMask;   // get 1 digit from the character
                    if(flag == 1) { 
                        if(x < image.getWidth()) {
                            image.setRGB(x, y, image.getRGB(x, y) | 0x00000001);    // store the bit which is 1 into a pixel's last digit
                            x++;
                        }
                        else {
                            x = 0;
                            y++;
                            image.setRGB(x, y, image.getRGB(x, y) | 0x00000001);    // store the bit which is 1 into a pixel's last digit
                        }
                    } 
                    else {  
                        if(x < image.getWidth()) {
                            image.setRGB(x, y, image.getRGB(x, y) & 0xFFFFFFFE);    // store the bit which is 0 into a pixel's last digit
                            x++;
                        }
                        else {
                            x = 0;
                            y++;
                            image.setRGB(x, y, image.getRGB(x, y) & 0xFFFFFFFE);    // store the bit which is 0 into a pixel's last digit
                        }
                    }
                    bit = bit >> 1;             // get the next digit from the character
                }           
            }
            
            // save the image which contains the secret information to another image file
            try {
                File outputfile = new File("textEmbedded.png"); 
                ImageIO.write(image, "png", outputfile);    
            } catch (IOException e) {
                
            }       
            return image;
        }
        
        // extract secret information/Text from a "cover image"
        public static void extractText(BufferedImage image, int length) {
            System.out.print("Extracting: ");
            int bitMask = 0x00000001;   // define the mask bit used to get the digit
            int x = 0;                  // define the starting pixel x
            int y = 0;                  // define the starting pixel y
            int flag;
            char[] c = new char[length] ;   // define a character array to store the secret information
            for(int i = 0; i < length; i++) {   
                int bit = 0;
                
                // 8 digits form a character
                for(int j = 0; j < 8; j++) {                
                    if(x < image.getWidth()) {
                        flag = image.getRGB(x, y) & bitMask;    // get the last digit of the pixel
                        x++;
                    }
                    else {
                        x = 0;
                        y++;
                        flag = image.getRGB(x, y) & bitMask;    // get the last digit of the pixel
                    }
                    
                    // store the extracted digits into an integer as a ASCII number
                    if(flag == 1) {                 
                        bit = bit >> 1; 
                        bit = bit | 0x80;
                    } 
                    else {                  
                        bit = bit >> 1;
                    }               
                }
                c[i] = (char) bit;  // represent the ASCII number by characters
                System.out.print(c[i]);
            }
        }
}

// Main.java

public class Main {
    
    public static void main(String[] args) throws Exception {
        try {
            BufferedImage coverImageText = ImageIO.read(new File("originalPic.png"));       
            String s = "Java is a popular programming language, created in 1995.";
            coverImageText = Steganography.embedText(coverImageText, s);                                // embed the secret information
            Steganography.extractText(ImageIO.read(new File("textEmbedded.png")), s.length()); // extract the secret information
        } catch(IOException e) {        
            System.out.print("Error: " + e);
        }   
    }
}

The problem is that it does extract most part correctly with exception of few characters. For this example in the console is displayed:

Extracting: Java is a popular progsamming langua'e, creaved in 1995.

I tried with different input texts but there are always some characters incorrectly extracted. The textEmbedded.png look like the original image with a bit different size as it should. Can someone please help me understand what is wrong here.


Solution

  • I edited it a bit and even though I am not sure why previous one didn't work... this one does for me:

    import java.awt.image.BufferedImage;
    import java.io.File;
    
    import javax.imageio.ImageIO;
    
    public class Steganography {
        // embed secret information/TEXT into a "cover image"
        public static void embedText(BufferedImage image, String s, String fileName) throws Exception {
            int n = s.length();
    
            if (n * 8 > image.getWidth() * image.getHeight()) {
                throw new Exception("The provided text is too large to be embedded in the image.");
            }
    
            int x = 0;
            int y = 0;
    
            for (int i = 0; i < n; i++) { // for every char in string
                char c = s.charAt(i);
                int binaryNum = (int) c; // get ASCII value
                for (int j = 0; j < 8; j++) { // write every bit from binaryNum
    
                    int bit = binaryNum & 0x00000001; // take LSB
                    binaryNum = binaryNum >> 1; // move to next digit
    
                    int colorByte = image.getRGB(x, y);
    
                    if (bit == 1) {
                        image.setRGB(x, y, colorByte | 0x00000001);
                    } else {
                        image.setRGB(x, y, colorByte & 0xFFFFFFFE);
                    }
                    
                    x++;
                    if (x >= image.getWidth()) {
                        x = 0;
                        y++;
                    }
    
                }
            }
            System.out.println();
            File outputfile = new File(fileName);   
            ImageIO.write(image, "png", outputfile);
        }
            
            // extract secret information/Text from a "cover image"
            public static void extractText(BufferedImage image, int length) {
                int x = 0, y = 0;
                
                for (int i = 0; i< length; i++) {
                    int binaryNum = 0;
                    for (int j = 0; j<8; j++) {
                        int colorByte = image.getRGB(x, y);
                        int bit = colorByte & 0x00000001;
                        binaryNum = binaryNum >> 1;
                        if (bit == 1) {
                            binaryNum = binaryNum | 0x80;
                        }
                        
                        
                        x++;
                        if (x >= image.getWidth()) {
                           x = 0;
                           y++;
                        }
    
                    }
                    
                    char c = (char) binaryNum;  
                    System.out.print(c);  
                }
            }
    }