Beyond the Visible: Exploring the Depths of Steganography
Steganography is the practice of concealing a message, file, image, or video within another message, file, image, or video. Unlike cryptography, which focuses on making a message unreadable to unauthorised parties, steganography aims to hide the message’s existence. The word “steganography” is derived from the Greek words “steganos,” meaning “covered,” and “graphein,” meaning “to write.”
An example from history
One notable historical example of steganography involves using invisible ink during the American Revolutionary War. The British and American forces employed various forms of steganography to conceal messages and strategic information.
In particular, the Culper Spy Ring, a clandestine network of American spies operating during the Revolutionary War, extensively used invisible ink to communicate covertly. One member of the ring, invisible ink expert James Jay, developed a secret method for creating invisible ink using simple household ingredients such as lemon juice or milk.
The Culper Spy Ring would write messages with this invisible ink between the lines of innocent-looking letters or on the blank spaces of documents. To reveal the hidden messages, the recipient would apply heat or special chemicals, causing the invisible ink to darken and become visible.
This use of steganography allowed the Culper Spy Ring to transmit crucial intelligence about British troop movements, plans, and other sensitive information without detection by British authorities. The effective use of invisible ink by the Culper Spy Ring played a significant role in aiding the American cause during the Revolutionary War and exemplifies the historical importance of steganography in espionage and covert operations.
Example of using Steganography in Cybersecurity
A modern example of steganography in cybersecurity involves concealing malicious code within seemingly innocuous digital files to bypass security measures and deliver malware to targeted systems. Cybercriminals often employ this technique to evade detection by traditional antivirus software and intrusion detection systems.
For instance, attackers may embed malicious payloads, such as Trojans or ransomware, within images, audio files, or documents using steganography techniques. The carrier files appear unchanged to the naked eye or standard file analysis tools, making it difficult for security solutions to detect hidden malware.
Once the steganographically encoded file reaches the target system, the attacker can extract and execute the concealed payload, thereby compromising the system and initiating malicious activities.
To combat this threat, cybersecurity professionals utilise advanced threat detection technologies capable of analysing files for signs of steganographic manipulation. These tools employ various techniques, such as statistical analysis, anomaly detection, and signature-based detection, to identify suspicious patterns or deviations from expected file structures.
Additionally, cybersecurity awareness training programs educate users about the risks associated with opening files from untrusted sources and emphasise the importance of maintaining up-to-date security software to mitigate the threat of steganography-based attacks.
All Code Examples are on GitHub: https://github.com/svenruppert/Steganography
How to do it practically in Java
Steganography techniques can involve various methods, such as:
The source code you will find on GitHub under the following URL:
Text Steganography:
Embedding secret messages within text documents by altering spacing, font styles, or using invisible characters.
Here’s a simple example of text steganography in Java using a basic technique called whitespace steganography. In this example, we’ll hide a secret message within a text document by manipulating the whitespace between words.
public class TextSteganographyExample { 4 5 public static void main(String[] args) { 6 String binaryData = "101"; // Binary data to hide 7 String sourceText = "Hello\nWorld\nThis is a test\n"; // Source text to hide data in 8 String hiddenText = hideData(sourceText, binaryData); 9 10 System.out.println("Text without hidden data:"); 11 System.out.println(sourceText); 12 System.out.println("Text with hidden data:"); 13 System.out.println(hiddenText); 14 15 //extract the hidden message 16 String extractedData = extractData(hiddenText); 17 System.out.println("extractedData = " + extractedData); 18 } 19 20 public static String hideData(String sourceText, String binaryData) { 21 Scanner scanner = new Scanner(sourceText); 22 StringBuilder hiddenTextBuilder = new StringBuilder(); 23 int index = 0; 24 while (scanner.hasNextLine() && index < binaryData.length()) { 25 String line = scanner.nextLine(); 26 // Append a space for '0', or a tab for '1' 27 char appendChar = binaryData.charAt(index) == '0' ? ' ' : '\t'; 28 hiddenTextBuilder.append(line).append(appendChar).append("\n"); 29 index++; 30 } 31 // If there's more of the source text, add it as is 32 while (scanner.hasNextLine()) { 33 hiddenTextBuilder.append(scanner.nextLine()).append("\n"); 34 } 35 scanner.close(); 36 return hiddenTextBuilder.toString(); 37 } 38 39 public static String extractData(String hiddenText) { 40 Scanner scanner = new Scanner(hiddenText); 41 StringBuilder binaryDataBuilder = new StringBuilder(); 42 while (scanner.hasNextLine()) { 43 String line = scanner.nextLine(); 44 if (line.endsWith("\t")) { 45 // If line ends with a tab, append '1' 46 binaryDataBuilder.append('1'); 47 } else if (line.endsWith(" ")) { 48 // If line ends with a space, append '0' 49 binaryDataBuilder.append('0'); 50 } 51 } 52 scanner.close(); 53 return binaryDataBuilder.toString(); 54 } 55 }
This code demonstrates a basic form of text steganography where a secret message is hidden within the whitespace of a text document. Note that this method needs to be revised and may not be suitable for high-security applications. Advanced techniques can involve more sophisticated manipulation of text or embedding data within specific patterns.
Image Steganography:
Data is concealed within digital images by modifying the least significant bits of pixel values or by embedding data within specific regions of the image where slight alterations are less noticeable.
Certainly! Here’s a simple example of image steganography in Java using LSB (Least Significant Bit) embedding. In this example, we’ll hide a secret message within the least significant bits of an image’s pixels.
This code hides a secret message within the least significant bit of each pixel in the image. The original image is saved as a new steganographic image when the message is hidden. Later, the hidden message is extracted by reading the steganographic image’s pixels and retrieving the least significant bit of the red component of each pixel. Finally, the binary message is converted back into a readable string. Replace “input_image.png” with the path to your input image.
public class ImageSteganography { 8 9 // Method to encode a message into an image 10 public static BufferedImage encodeMessage(BufferedImage image, String message) { 11 int messageLength = message.length(); 12 int imageWidth = image.getWidth(); 13 int imageHeight = image.getHeight(); 14 int[] imagePixels = new int[imageWidth * imageHeight]; 15 image.getRGB(0, 0, imageWidth, imageHeight, imagePixels, 0, imageWidth); 16 17 // Convert the message into a binary string 18 StringBuilder binaryMessage = new StringBuilder(); 19 for (char character : message.toCharArray()) { 20 binaryMessage.append(String.format("%8s", Integer.toBinaryString(character)).replaceAll(" ", "0")); 21 } 22 23 // Encode the message length at the beginning 24 String messageLengthBinary = String.format("%32s", Integer.toBinaryString(messageLength)).replace(' ', '0'); 25 binaryMessage.insert(0, messageLengthBinary); 26 27 // Encode the binary message into the image 28 for (int i = 0; i < binaryMessage.length(); i++) { 29 int pixel = imagePixels[i]; 30 int blue = pixel & 0xFF; 31 int green = (pixel >> 8) & 0xFF; 32 int red = (pixel >> 16) & 0xFF; 33 int alpha = (pixel >> 24) & 0xFF; 34 35 // Modify the LSB of the blue part of the pixel to match the current bit of the message 36 blue = (blue & 0xFE) | (binaryMessage.charAt(i) - '0'); 37 int newPixel = (alpha << 24) | (red << 16) | (green << 8) | blue; 38 39 imagePixels[i] = newPixel; 40 } 41 42 BufferedImage newImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); 43 newImage.setRGB(0, 0, imageWidth, imageHeight, imagePixels, 0, imageWidth); 44 return newImage; 45 } 46 // Method to decode the hidden message from an image 47 public static String decodeMessage(BufferedImage image) { 48 int imageWidth = image.getWidth(); 49 int imageHeight = image.getHeight(); 50 int[] imagePixels = new int[imageWidth * imageHeight]; 51 image.getRGB(0, 0, imageWidth, imageHeight, imagePixels, 0, imageWidth); 52 53 // Extract the length of the message 54 StringBuilder messageLengthBinary = new StringBuilder(); 55 for (int i = 0; i < 32; i++) { 56 int pixel = imagePixels[i]; 57 int blue = pixel & 0xFF; 58 messageLengthBinary.append(blue & 1); 59 } 60 int messageLength = Integer.parseInt(messageLengthBinary.toString(), 2); 61 62 // Extract the binary message from the image 63 StringBuilder binaryMessage = new StringBuilder(); 64 for (int i = 32; i < 32 + messageLength * 8; i++) { 65 int pixel = imagePixels[i]; 66 int blue = pixel & 0xFF; 67 binaryMessage.append(blue & 1); 68 } 69 70 // Convert the binary message to string 71 StringBuilder message = new StringBuilder(); 72 for (int i = 0; i < binaryMessage.length(); i += 8) { 73 String byteString = binaryMessage.substring(i, i + 8); 74 int charCode = Integer.parseInt(byteString, 2); 75 message.append((char) charCode); 76 } 77 78 return message.toString(); 79 } 80 public static void main(String[] args) throws IOException { 81 File originalImageFile = new File("_data/_DSC1259.png"); // Specify the path to the input image 82 BufferedImage originalImage = ImageIO.read(originalImageFile); 83 String secretMessage = "Secret message goes here"; 84 85 // Encode the message into the image 86 BufferedImage encodedImage = encodeMessage(originalImage, secretMessage); 87 88 // Save the encoded image 89 File outputImageFile = new File("_data/_DSC1259_with-data.png"); // Specify the path to the output image 90 ImageIO.write(encodedImage, "png", outputImageFile); 91 System.out.println("The message has been encoded into the image."); 92 93 94 File imageFile = new File("_data/_DSC1259_with-data.png"); // Specify the path to the encoded image 95 BufferedImage image = ImageIO.read(imageFile); 96 97 // Decode the message from the image 98 String decodedMessage = decodeMessage(image); 99 System.out.println("The hidden message is: " + decodedMessage); 100 calculatingMaxInfoAmount("_data/_DSC1259_with-data.png"); 101 } 102 private static void calculatingMaxInfoAmount(String pathname){ 103 try { 104 File imageFile = new File(pathname); // Specify the path to your image 105 BufferedImage image = ImageIO.read(imageFile); 106 int imageWidth = image.getWidth(); 107 int imageHeight = image.getHeight(); 108 long maxBits = (long) imageWidth * imageHeight; // Maximum bits that can be stored 109 long maxBytes = maxBits / 8; // Convert bits to bytes 110 111 System.out.println("Maximum information that can be stored in bytes: " + maxBytes); 112 } catch (IOException e) { 113 throw new RuntimeException(e); 114 } 115 } 116 }
Audio Steganography:
Hiding information within audio files by modifying the least significant bits of audio samples or by exploiting imperceptible frequencies.
Here’s a basic example of audio steganography in Java using LSB (Least Significant Bit) embedding. In this example, we’ll hide a secret message within the least significant bits of the audio samples.
public class AudioSteganography { 6 7 public static void main(String[] args) { 8 File inputFile = new File("_data/Security - 2024.06 - What is Steganography-16bit-single-track_A01_L.wav"); 9 File outputFile = new File("_data/output.wav"); 10 String message = "Secret Message"; 11 hideMessage(inputFile, outputFile, message); 12 13 File inputFileEncoded = new File("_data/output.wav"); // This is the file with the hidden message 14 String extractedMessage = extractMessage(inputFileEncoded, 14); // Assuming we know the message length 15 System.out.println("Extracted Message: " + extractedMessage); 16 } 17 18 public static void hideMessage(File inputFile, File outputFile, String message) { 19 try { 20 // Convert the message to a binary string 21 byte[] messageBytes = message.getBytes(); 22 StringBuilder binaryMessage = new StringBuilder(); 23 for (byte b : messageBytes) { 24 binaryMessage.append(String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0')); 25 } 26 String messageBinary = binaryMessage.toString(); 27 28 // Load the audio file 29 AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputFile); 30 AudioFormat format = audioInputStream.getFormat(); 31 byte[] audioBytes = audioInputStream.readAllBytes(); 32 33 // Hide the message in the LSB of the audio bytes 34 int messageIndex = 0; 35 for (int i = 0; i < audioBytes.length && messageIndex < messageBinary.length(); i += 2) { // Skip every other byte for 16-bit samples 36 if (messageBinary.charAt(messageIndex) == '1') { 37 audioBytes[i] = (byte) (audioBytes[i] | 1); // Set LSB to 1 38 } else { 39 audioBytes[i] = (byte) (audioBytes[i] & ~1); // Set LSB to 0 40 } 41 messageIndex++; 42 } 43 44 // Write the modified samples to a new file 45 ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes); 46 AudioInputStream outputAudioInputStream = new AudioInputStream(bais, format, audioBytes.length / format.getFrameSize()); 47 AudioSystem.write(outputAudioInputStream, AudioFileFormat.Type.WAVE, outputFile); 48 49 System.out.println("The message has been hidden in " + outputFile.getName()); 50 51 } catch (UnsupportedAudioFileException | IOException e) { 52 e.printStackTrace(); 53 } 54 } 55 56 public static String extractMessage(File inputFile, int messageLength) { 57 try { 58 // Load the audio file 59 AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputFile); 60 byte[] audioBytes = audioInputStream.readAllBytes(); 61 62 // Extract bits to reconstruct the message binary string 63 StringBuilder messageBinary = new StringBuilder(); 64 for (int i = 0; i < messageLength * 8 * 2; i += 2) { // Assuming 16-bit samples, adjust for actual sample size 65 byte b = audioBytes[i]; 66 int lsb = b & 1; // Extract the LSB 67 messageBinary.append(lsb); 68 } 69 70 // Convert the binary string to text 71 StringBuilder message = new StringBuilder(); 72 for (int i = 0; i < messageBinary.length(); i += 8) { 73 String byteString = messageBinary.substring(i, i + 8); 74 int charCode = Integer.parseInt(byteString, 2); 75 message.append((char) charCode); 76 } 77 78 return message.toString(); 79 80 } catch (UnsupportedAudioFileException | IOException e) { 81 e.printStackTrace(); 82 } 83 84 return null; 85 } 86 }
This code hides a secret message within the input audio file’s least significant bit of each audio sample. The modified audio data is then saved as a new steganographic audio file. Later, the hidden message is extracted by reading the least significant bit of each audio sample in the steganographic audio file. Replace filename and path with the path to your input audio file.
Video Steganography:
Concealing data within video files by manipulating frames or embedding data in specific segments.
Below is a basic example of video steganography in Java using LSB (Least Significant Bit) embedding. In this example, we’ll hide a secret message within the least significant bits of the blue pixel values of video frames.
public class VideoSteganography { 14 //Should be extracted from the orig video 15 public static final int FPS = 50; 16 public static final String INPUT_FILE = "input.mp4"; 17 public static final String OUTPUT_FILE = "output.mp4"; 18 19 public static void main(String[] args) throws Exception { 20 FileChannelWrapper fileChannelWrapperIN = NIOUtils.readableChannel(new File(INPUT_FILE)); 21 File outputFile = new File(OUTPUT_FILE); 22 // Create a SequenceEncoder for the output file at 25 frames per second 23 SequenceEncoder encoder = SequenceEncoder.createSequenceEncoder(outputFile, FPS); 24 FrameGrab grab = FrameGrab.createFrameGrab(fileChannelWrapperIN); 25 Picture picture; 26 // to improve to process 27 // 0. detect the FPS from the input video 28 // 1. calculate the max amount of possible bytes that can be encoded 29 // 2. calculate the max amount of bytes that can be stored in each frame 30 // 3. split the message into chunks to fit into a frame 31 // 4. define a sequence to mark the end of the message 32 33 while (null != (picture = grab.getNativeFrame())) { 34 // Here, convert the Picture to BufferedImage 35 BufferedImage frame = AWTUtil.toBufferedImage(picture); 36 // Modify the frame to encode part of the message 37 BufferedImage encodedMessageOnBlue = encodeMessageOnBlue(frame, toBinaryString("Hello Message")); 38 // Convert BufferedImage to Picture (required by JCodec) 39 Picture pic = AWTUtil.fromBufferedImage(encodedMessageOnBlue, ColorSpace.RGB); 40 // Encode the modified frame back into the video 41 encoder.encodeNativeFrame(pic); 42 } 43 // Finalize video encoding and clean up 44 NIOUtils.closeQuietly(fileChannelWrapperIN); 45 // Finalize and close the encoder (important!) 46 encoder.finish(); 47 } 48 49 private static String toBinaryString(String message) { 50 StringBuilder binary = new StringBuilder(); 51 for (char character : message.toCharArray()) { 52 binary.append(String 53 .format("%8s", Integer.toBinaryString(character)) 54 .replace(' ', '0')); 55 } 56 return binary.toString(); 57 } 58 59 private static BufferedImage encodeMessageOnBlue(BufferedImage image, String binaryMessage) { 60 int messageIndex = 0; 61 int messageLength = binaryMessage.length(); 62 63 for (int y = 0; y < image.getHeight() && messageIndex < messageLength; y++) { 64 for (int x = 0; x < image.getWidth() && messageIndex < messageLength; x++) { 65 int pixel = image.getRGB(x, y); 66 int alpha = (pixel >> 24) & 0xFF; 67 int red = (pixel >> 16) & 0xFF; 68 int green = (pixel >> 8) & 0xFF; 69 int blue = pixel & 0xFF; 70 71 // Modify the LSB of the blue component 72 blue = (blue & 0xFE) | (binaryMessage.charAt(messageIndex) - '0'); 73 messageIndex++; 74 75 // Reconstruct the pixel and set it back 76 int newPixel = (alpha << 24) | (red << 16) | (green << 8) | blue; 77 image.setRGB(x, y, newPixel); 78 } 79 } 80 return image; 81 } 82 }
File Steganography:
Embedding files within other files, such as hiding a document within another document.
File steganography with PDF files involves hiding one PDF file within another PDF file. In this example, we’ll hide the contents of one PDF file within the metadata of another PDF file. Specifically, we’ll utilise the “Keywords” metadata field of PDFs to embed the content of a secret PDF file.
Here’s the Java code to achieve this using the Apache PDFBox library:
9 10 public class HideMessageInPDF { 11 public static void main(String[] args) { 12 try { 13 // Load an existing PDF document 14 File file = new File("_data/ReadME.pdf"); 15 PDDocument document = PDDocument.load(file); 16 // Retrieve the document's metadata 17 PDDocumentInformation info = document.getDocumentInformation(); 18 // Add a hidden message to the metadata 19 // You could use a less obvious key than "HiddenMessage" to make it less detectable 20 info.setCustomMetadataValue("HiddenMessage", "This is a secret message"); 21 // Save the modified document 22 document.save("_data/ReadME_with-data.pdf"); 23 document.close(); 24 System.out.println("Hidden message added to the PDF metadata."); 25 } catch (IOException e) { 26 e.printStackTrace(); 27 } 28 29 try { 30 // Load the PDF document 31 File file = new File("_data/ReadME_with-data.pdf"); 32 PDDocument document = PDDocument.load(file); 33 // Access the document's metadata 34 PDDocumentInformation info = document.getDocumentInformation(); 35 // Retrieve the hidden message from the metadata 36 String hiddenMessage = info.getCustomMetadataValue("HiddenMessage"); 37 System.out.println("Hidden message: " + hiddenMessage); 38 document.close(); 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 } 43 }
In this code:
1. The “hidePDF” function loads the source PDF document, converts the content of the secret PDF file to a Base64 encoded string, and embeds it into the “Keywords” metadata field of the source PDF file.
2. The “extractPDF” function loads the steganographic PDF document, extracts the Base64 encoded content from the “Keywords” metadata field, decodes it, and writes it to an output PDF file.
Make sure to replace every path to files with the appropriate file paths in your system. Additionally, you’ll need to include the Apache PDFBox library in your project to use its functionalities.
Conclusion:
As explored in this paper, steganography is a fascinating field with a rich history and diverse applications. By concealing information from ordinary data, steganography provides a powerful means of covert communication and data protection. Steganography continues to evolve alongside technological advancements, from ancient techniques of hiding messages in wax tablets to modern digital methods embedded within multimedia files.
While steganography offers numerous benefits in fields like digital watermarking, copyright protection, and secure communication, it also presents challenges and ethical considerations. Due to its subtle nature, detecting steganographic content remains a significant challenge, requiring advanced algorithms and tools for effective analysis.
As technology continues to advance, the importance of understanding steganography becomes increasingly critical. It underscores the need for robust security measures balanced with respect for privacy rights. By delving into the principles and techniques of steganography, researchers and practitioners can develop more effective strategies for exploiting and defending against covert communication methods.
In conclusion, steganography represents a dynamic and intriguing aspect of information security, with implications spanning various domains. By embracing its complexities and exploring its potential applications, we can navigate the intricate landscape of digital communication with greater insight and resilience.