Hallo,
ich habe ein Programm zur Steganographie geschrieben, das Dateien in Bildern verstecken kann. Akzeptiert wird jede Datei, das Bild muss ein .png sein, da nur dieses Format einen echten Alpha-Channel hat.
Verschlüsselung funktioniert so : Ein Pixel besteht ja aus 4 Bytes (Rot, Grün, Blau, Alpha). Die zu verschlüsselnde Datei wird bitweise durchgegangen, das Bild byteweise. Ist das Datei-Bit gesetzt (1) wird das Bild-Byte um 1 erhöht (bzw. um 1 subtrahiert, wenn es nicht mehr erhöht werden kann), ist das Bit nicht gesetzt (0) bleibt das Byte so.
Auf diese Weise kann man ein Byte in zwei Pixeln verschlüsseln und die Farben sollten sich eigentlich nicht sichtbar verändern, da man eine Farbkomponente ja nur um 1/255 ändert.
Inzwischen funktioniert die Verschlüsselung und Entschlüsselung wunderbar, allerdings ist der verschlüsselte Bereich des Bildes von schwarzen Pixeln durchzogen, womit die Veränderung des Bildes sofort auffällt.
Entdeckt vielleicht einer von euch den Fehler?
Ein Punkt könnte die Tatsache sein, dass Java ja nur Datentypen mit Vorzeichen kennt und deswegen eine Farbkomponente von -127 bis +128 gespeichert wird anstatt von 0 bis 255. Allerdings sollte das kein Problem darstellen, oder?
Hier sieht man das Resultat mit den falschen Pixeln am oberen Rand. Beim Uploader werden diese komischerweise weiß dargestellt.
Codierung:
Decodierung:
ich habe ein Programm zur Steganographie geschrieben, das Dateien in Bildern verstecken kann. Akzeptiert wird jede Datei, das Bild muss ein .png sein, da nur dieses Format einen echten Alpha-Channel hat.
Verschlüsselung funktioniert so : Ein Pixel besteht ja aus 4 Bytes (Rot, Grün, Blau, Alpha). Die zu verschlüsselnde Datei wird bitweise durchgegangen, das Bild byteweise. Ist das Datei-Bit gesetzt (1) wird das Bild-Byte um 1 erhöht (bzw. um 1 subtrahiert, wenn es nicht mehr erhöht werden kann), ist das Bit nicht gesetzt (0) bleibt das Byte so.
Auf diese Weise kann man ein Byte in zwei Pixeln verschlüsseln und die Farben sollten sich eigentlich nicht sichtbar verändern, da man eine Farbkomponente ja nur um 1/255 ändert.
Inzwischen funktioniert die Verschlüsselung und Entschlüsselung wunderbar, allerdings ist der verschlüsselte Bereich des Bildes von schwarzen Pixeln durchzogen, womit die Veränderung des Bildes sofort auffällt.
Entdeckt vielleicht einer von euch den Fehler?
Ein Punkt könnte die Tatsache sein, dass Java ja nur Datentypen mit Vorzeichen kennt und deswegen eine Farbkomponente von -127 bis +128 gespeichert wird anstatt von 0 bis 255. Allerdings sollte das kein Problem darstellen, oder?
Hier sieht man das Resultat mit den falschen Pixeln am oberen Rand. Beim Uploader werden diese komischerweise weiß dargestellt.
Codierung:
Java:
public void encode(File sourceFile, BufferedImage keyImage, File targetFile) {
if(sourceFile == null || keyImage == null) throw new IllegalArgumentException("Arguments are not allowed to be null.");
if(targetFile.isDirectory()) throw new IllegalArgumentException("Target has to be a non-directory file.");
if(!sourceFile.exists() || sourceFile.isDirectory()) throw new IllegalArgumentException("Source has to be an existing non-directory file.");
//+ 8 for the length long and *2 because one pixel can only store a half byte
if((sourceFile.length() + 8) * 2 > keyImage.getWidth() * keyImage.getHeight()) throw new IllegalArgumentException("Key image is too small.");
BufferedImage targetImage = new BufferedImage(keyImage.getWidth(), keyImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
copyImage(keyImage, targetImage);
byte[] fileDataBytes = fileHandler.getFileBytes(sourceFile);
byte[] fileLengthBytes = binary.longToByteArray(sourceFile.length());
//Writing the file name: int for the string length, then the string bytes
byte[] stringBytes = sourceFile.getName().getBytes();
byte[] stringLengthBytes = binary.intToByteArray(stringBytes.length);
//Writing the length as long --> 8 bytes
int index = 0;
encodeBytesToImage(stringLengthBytes, targetImage, index);
index += stringLengthBytes.length * 2;
encodeBytesToImage(stringBytes, targetImage, index);
index += stringBytes.length * 2;
encodeBytesToImage(fileLengthBytes, targetImage, index);
index += fileLengthBytes.length * 2;
encodeBytesToImage(fileDataBytes, targetImage, index);
/*System.out.println("Encoded name: " + new String(stringBytes));
System.out.println("Encoded length: " + sourceFile.length());*/
try {
ImageIO.write(targetImage, "png", targetFile);
} catch (IOException e) {
e.printStackTrace();
gui.showErrorMessage("IO-Fehler", "Fehler beim Schreiben der Bilddatei.");
}
//System.out.println("Image encoding successfull");
}
private void encodeBytesToImage(byte[] data, BufferedImage image, int offset) {
for(int i = 0; i < data.length * 2; i += 2) {
encodeByteToImage(data[i / 2], image, i + offset);
}
}
private void encodeByteToImage(byte data, BufferedImage image, int index) {
Point pixel = getPointFromIndex(index, image.getWidth(), image.getHeight());
image.setRGB(pixel.x, pixel.y, encodeHalfByteToPixel(image.getRGB(pixel.x, pixel.y), data, 0));
pixel = getPointFromIndex(index + 1, image.getWidth(), image.getHeight());
image.setRGB(pixel.x, pixel.y, encodeHalfByteToPixel(image.getRGB(pixel.x, pixel.y), data, 4));
}
private int encodeHalfByteToPixel(int rgb, byte value, int offset) {
if(offset != 0 && offset != 4) throw new IllegalArgumentException("Offset has to be 0 or 4.");
byte[] colorBytes = binary.intToByteArray(rgb);
for(int i = 0; i < 4; i++) {
if(binary.isBitSet(value, i + offset)) colorBytes[i] = (byte) (colorBytes[i] > -128 ? colorBytes[i] + 1 : colorBytes[i] - 1);
}
return binary.byteArrayToInt(colorBytes);
}
Java:
public void decode(BufferedImage sourceImage, BufferedImage keyImage, File targetDirectory) {
if(sourceImage == null || keyImage == null) throw new IllegalArgumentException("Arguments are not allowed to be null.");
if(!targetDirectory.isDirectory()) throw new IllegalArgumentException("Target has to be a directory.");
byte[] fileDataBytes;
long fileLength;
int fileNameLength;
String fileName = null;
int index = 0;
fileNameLength = binary.byteArrayToInt(decodePixels(sourceImage, keyImage, BinaryOperations.BYTES_INT, index));
index += BinaryOperations.BYTES_INT * 2;
fileName = new String(decodePixels(sourceImage, keyImage, fileNameLength, index));
index += fileNameLength * 2;
fileLength = binary.byteArrayToLong(decodePixels(sourceImage, keyImage, BinaryOperations.BYTES_LONG, index));
index += BinaryOperations.BYTES_LONG * 2;
fileDataBytes = decodePixels(sourceImage, keyImage, (int) fileLength, index);
File targetFile = new File(targetDirectory.getPath() + "/" + fileName);
fileHandler.writeBytesToFile(targetFile, fileDataBytes);
/*System.out.println("Decoded length: " + fileLength);
System.out.println("Decoded file " + targetFile);
System.out.println("Image decoding successfull");*/
}
private byte[] decodePixels(BufferedImage sourceImage, BufferedImage keyImage, int length, int offset) {
byte[] data = new byte[length];
for(int i = offset; i < length * 2 + offset; i += 2) {
Point pixel = getPointFromIndex(i, sourceImage.getWidth(), sourceImage.getHeight());
int firstKey, firstMessage, secondKey, secondMessage;
firstKey = keyImage.getRGB(pixel.x, pixel.y);
firstMessage = sourceImage.getRGB(pixel.x, pixel.y);
pixel = getPointFromIndex(i + 1, sourceImage.getWidth(), sourceImage.getHeight());
secondKey = keyImage.getRGB(pixel.x, pixel.y);
secondMessage = sourceImage.getRGB(pixel.x, pixel.y);
data[(i - offset) / 2] = decodeTwoPixels(firstKey, firstMessage, secondKey, secondMessage);
}
return data;
}
private byte decodeTwoPixels(int firstKey, int firstMessage, int secondKey, int secondMessage) {
byte[] firstKeyBytes = binary.intToByteArray(firstKey), firstMessageBytes = binary.intToByteArray(firstMessage);
byte[] secondKeyBytes = binary.intToByteArray(secondKey), secondMessageBytes = binary.intToByteArray(secondMessage);
byte result = 0;
for(int i = 0; i < 4; i++) {
result = binary.setBit(result, i, firstKeyBytes[i] != firstMessageBytes[i] ? true : false);
}
for(int i = 4; i < 8; i++) {
result = binary.setBit(result, i, secondKeyBytes[i - 4] != secondMessageBytes[i - 4] ? true : false);
}
return result;
}