Zusammenfassung
Mehr ist zum Speichern einer geheimen Botschaft in einem Bild nicht notwendig. Das Auslesen funktioniert selbstverständlich genau in die andere Richtung. Alle Bits der Farbkanäle der Pixel des Bildes werden zu kompletten Bytes aneinander gereiht. Diese extrahierten Bytes bilden die ursprüngliche Nachricht. Da diese Vorgehensweise nun sicherlich auch für Sie kein Hexenwerk mehr ist, verzichte ich an dieser Stelle auf eine detaillierte Beschreibung, sondern beschränke mich auf den zusammengefassten Code mitsamt Kommentaren.
package de.jbb.steganographie;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
public class HideInPicture {
public BufferedImage hideMessage(BufferedImage img, String message) {
// Zeichen für Message-Ende hinzufügen
message += (char)0;
// Text in Bytes umwandeln
byte[] b = message.getBytes();
// Farbkanal
Color channel = Color.RED;
// Alle Bytes durchlaufen
for (int i = 0, x = 0, y = 0; i < b.length; i++) {
// Alle Bits durchlaufen
for (int j = 7; j > -1; j--) {
// Wert des Bits auslesen
int bit = ((b[i] & 0xFF) >> j) & 1;
// Farbe an der aktuellen Position auslesen
int rgb = img.getRGB(x, y);
// Den aktuellen Farbkanal auslesen
int color = (rgb >> channel.getShift()) & 0xFF;
// Farbkanal manipulieren
if ((color & 1) != bit) {
// Den ausgelesenen Farbkanal der Farbe auf 0 setzen
rgb &= channel.getRGBManipulator();
switch (bit) {
case 1:
color = color + 1;
break;
default:
color = color - 1;
}
// Farbkanal zurückschreiben
rgb |= color << channel.getShift();
img.setRGB(x, y, rgb);
}
// nächsten Farbkanal setzen
channel = channel.getNext();
// Falls Farbkanal = RED => X-Position verändern
if (channel.equals(Color.RED)) {
x++;
// Falls x größer als Breite des Bildes => Y-Positon verändern
if (x >= img.getWidth()) {
x = 0;
y++;
// Falls y größer als Höhe des Bildes => Fehler
if (y >= img.getHeight()) {
return null;
}
}
}
}
}
return img;
}
public String extractMessage(BufferedImage img) {
// ArrayList für gelesene Bytes
ArrayList<Byte> bytes = new ArrayList<Byte>();
// Alle horizontalen Pixel durchlaufen
for (int y = 0, count = 7, value = 0; y < img.getHeight(); y++) {
// Alle vertikalen Pixel durchlaufen
for (int x = 0; x < img.getWidth(); x++) {
// Aktuelle Farbe auslesen
int rgb = img.getRGB(x, y);
// Alle Farbkanäle durchlaufen
for (Color c : Color.values()) {
// Aktuelles Byte befüllen
value |= (((rgb >> c.getShift()) & 0xFF) & 1) << count--;
// Aktuelles Byte ist voll
if (count == -1) {
// Byte == 0 (Nachrichtende)
if ((byte)value == 0) {
// Nachricht in String umwandeln und zurückgeben
return bytesToString(bytes.toArray(new Byte[0]));
}
// Byte-ArrayList das aktuelle Byte hinzufügen
bytes.add((byte)value);
// Zählvariablen zurücksetzen
value = 0;
count = 7;
}
}
}
}
return bytesToString(bytes.toArray(new Byte[0]));
}
private String bytesToString(Byte[] barr) {
byte[] barrPrim = new byte[barr.length];
for (int i = 0; i < barr.length; i++) {
barrPrim[i] = barr[i];
}
return new String(barrPrim);
}
}
Damit Sie alles beisammen haben, finden Sie hier auch noch einmal den Code unserer Color-Enumeration.
package de.jbb.steganographie;
public enum Color {
RED(16, ( 255 << 24 ) | ( 255 << 8 ) | ( 255 << 0 )),
GREEN(8, ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 0 )),
BLUE(0, ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ));
private int shift = 0;
private int rgbManipulator;
private Color(int shift, int rgbManipulator) {
this.shift = shift;
this.rgbManipulator = rgbManipulator;
}
public Color getNext() {
if (this == RED) {
return GREEN;
}
else if (this == GREEN) {
return BLUE;
}
else {
return RED;
}
}
public int getShift() {
return this.shift;
}
public int getRGBManipulator() {
return this.rgbManipulator;
}
}
Die Klasse könnten Sie bspw. so testen (Vorausgesetzt, dass auf „C:\“ ein Bild mit dem Namen „test.png“ liegt):
public static void main(String[] args) throws Exception {
HideInPicture hip = new HideInPicture();
BufferedImage img = hip.hideMessage(ImageIO.read(new File("C:/test.PNG")), "Abc23*ß");
ImageIO.write(img, "png", new File("C:/test2.png"));
System.out.println(hip.extractMessage(ImageIO.read(new File("C:/test2.PNG"))));
}
Sie können eine mögliche Implementierung von diesem Algorithmus auf SoftK.de als Opensource kostenfrei herunterladen.
