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.
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);
}
}
}