XOR funktioniert mit Text-, aber nicht Binärdateien

Kai008

Erfahrenes Mitglied
Ich will ein kleines Programm schreiben, was eine Datei mit einen Schlüssel XOR-Verknüpft. Hat keinen tieferen Sinn, nur zum Spaß.
Bei Textdateien funktioniert es gut, nur bei Binärdateien kommen manchmal falsche Byte's raus.
Und ich habe keine Ahnung warum es bei den einen funktioniert und bei den anderen nicht.
Die Datei wird um zu verhindern dass der RAM ausgeht in 32Bit-Blöcken gelesen/verarbeitet/geschrieben.


Diese Klasse chiffriert die Blöcke.
Java:
package crypt;

class Crypt
{
	static String run(String block, Password password)
	{
		password.setPasswordLenght(block.length());
		String pass = password.getPassword();
		
		String result = null;
		String cryptText = null;
		
		for(int i = 0; i < block.length(); i++)
		{
			int blockPiece = (int)block.substring(i, i + 1).charAt(0);
			int passPiece = (int)pass.substring(i, i + 1).charAt(0);
			
			String binaryBlockPiece = Integer.toBinaryString(blockPiece);
			while(binaryBlockPiece.length() < 8)
				binaryBlockPiece = "0".concat(binaryBlockPiece);
			
			String binaryPassPiece = Integer.toBinaryString(passPiece);
			while(binaryPassPiece.length() < 8)
				binaryPassPiece = "0".concat(binaryPassPiece);
			
			for(int j = 0; j < 8; j++)
			{
				char binaryBlockPieceChar = binaryBlockPiece.charAt(j);
				char binaryPassPieceChar = binaryPassPiece.charAt(j);
				
				String value = "1";
				if(binaryBlockPieceChar != binaryPassPieceChar)
					value = "0";
				
				if(cryptText == null)
					cryptText = value;
				else
					cryptText = cryptText.concat(value);
			}
		}
		
		for(int i = 0; i < cryptText.length(); i+=8)
		{
			int cryptTextPart = (int)Integer.parseInt(
					cryptText.substring(i, i + 8), 2);
			
			if(result == null)
				result = String.valueOf((char)cryptTextPart);
			else
				result = result.concat(String.valueOf((char)cryptTextPart));
		}
		return(result);
	}
}

Diese Klasse (die einzige public im Package) startet den Vorgang.
Java:
package crypt;

import java.io.File;

public class StartCrypt
{
	public StartCrypt(File f, String string)
	{
		ReadFile readFile = new ReadFile();
		readFile.connect(f);
	
		WriteFile writeFile = new WriteFile();
		writeFile.connect(f);
		Password pass = new Password(string);
		String readLine = null;
		while((readLine = readFile.read(f, 32, ReadFile.RETURN_TYP_AS_STRING))
				!= null)
		{
			String cryptText = Crypt.run(readLine, pass);
			writeFile.write(cryptText);
		}
		readFile.close();
		writeFile.close();
	}
}

Diese ließt die Blöcke nacheinander ein:
Java:
package crypt;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

class ReadFile
{
	private FileInputStream fileInputStream;
	private BufferedInputStream bufferedInputStream;
	
	static final byte RETURN_TYP_AS_STRING = 0;
	
	public ReadFile()
	{
		super();
	}
	public void connect(File fileToOpen)
	{
		if(!fileToOpen.exists())
		{
			new FileNotFoundException("File not Found").printStackTrace();
			return;
		}
		
		try
		{
			this.fileInputStream = new FileInputStream(fileToOpen);
			this.bufferedInputStream = new BufferedInputStream(
					this.fileInputStream);
		}
		catch(Exception e)
		{
			// TODO: handle exception
		}
	}
	public String read(File fileToOpen, int blockLenght, byte returnTyp)
	{
		String line = null;

		try
		{
			int readChar = 0;
			for(int i = 0; i < blockLenght &&
			((readChar = this.bufferedInputStream.read()) != -1); i++)
			{
				if(returnTyp == ReadFile.RETURN_TYP_AS_STRING)
				{
					if(line == null)
						line = String.valueOf((char)readChar);
					else
						line = line.concat(String.valueOf((char)readChar));
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return(line);
	}
	public void close()
	{
		try
		{
			this.bufferedInputStream.close();
			this.fileInputStream.close();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

Diese enthält einfach nur das Passwort.
Java:
package crypt;

class Password
{
	private String password;
	
	Password(String pass)
	{
		super();
		this.password = pass;
	}
	public void setPasswordLenght(int keylenght)
	{
		if(this.password.length() != keylenght)
		{
			while(this.password.length() < keylenght)
				this.password = this.password.concat(this.password);
			this.password = this.password.substring(0, keylenght);
		}
	}
	public String getPassword()
	{
		return(this.password);
	}
}

Und diese schreibt anschließend die neuen Blöcke in die neue Datei:
Java:
package crypt;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;

class WriteFile
{
	private FileOutputStream fileOutputStream;
	private BufferedOutputStream bufferedOutputStream;

	WriteFile()
	{
		super();
	}
	public void connect(File fileToOpen)
	{
		File newFile = new File(fileToOpen.getAbsolutePath().concat(".crypt"));
		if(newFile.exists())
			newFile.delete();
		try
		{
			newFile.createNewFile();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
		try
		{
			this.fileOutputStream = new FileOutputStream(newFile);
			this.bufferedOutputStream = new BufferedOutputStream(
					this.fileOutputStream);
		}
		catch(Exception e)
		{
			// TODO: handle exception
		}
	}
	public void write(String toWrite)
	{
		try
		{
			this.bufferedOutputStream.write(toWrite.getBytes());
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	public void close()
	{
		try
		{
			this.bufferedOutputStream.flush();
			this.fileOutputStream.flush();
			this.bufferedOutputStream.close();
			this.fileOutputStream.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}


Hat zufällig irgendjemand eine Idee, woran das liegen könnte?
 
Moin,

beschreib' doch einfach mal genau, an welcher Stelle in den vielen Zeilen welches Problem auftritt ! ;-]

Danke und Gruß
Klaus
 
Eben das ist das Problem, es ist ein logischer Bug.
Aber vermutlich liegt der Bug in der Klasse "Crypt", also dass das Verknüpfen falsch läuft.
z. B. wenn ich

http://www.tutorials.de
77 77 77 2E 74 75 74 6F 72 69 61 6C 2E 64 65 (in Hex)

mit den Passwort "tutorials" verändere, kommt

üýü¾ùãêüþâëç¾éó
FC FD FC BE F9 E3 EA FC FE E2 EB E7 BE E9 F3


raus.
Wenn ich das Programm nochmal drüberlaufen lasse, kommt wieder exakt ersteres heraus.


Aber wenn ich z. B. ein Bild verändere, was mit folgenden anfängt:

ÿØÿà..JFIF.....d.d..ÿì..Ducky..........ÿî.!Adobe.dÀ..............=..2Ç..‹KÿÛ.„......................................#$&$#.//22// (Punkte bedeuten Steuerzeichen)
FF D8 FF E0 00 10 4A 46 49 46 00 01 02 00 00 64 00 64 00 00 FF EC 00 11 44 75 63 6B 79 00 01 00 04 00 00 00 1F 00 00 FF EE 00 21 41 64 6F 62 65 00 64 C0 00 00 00 01 03 00 10 03 02 03 06 00 00 14 3D 00 00 32 C7 00 00 8B 4B FF DB 00 84 00 10 0B 0B 0B 0C 0B 10 0C 0C 10 17 0F 0D 0F 17 1A 14 10 10 14 1A 1E 17 17 17 17 17 1E 1D 17 1A 19 19 1A 17 1D 1D 23 24 26 24 23 1D 2F 2F 32 32 2F 2F

kommt folgendes heraus:

tRtpÔÕÅÍ?ú?èt|Úæïàólb?«Êôâôû?èK·¿Q.ÀuP?.¨´«²½?£¤¸¹¿¢
6C 48 6C 73 3F 3F D9 D6 DA D5 3F 3F 3F 3F 3F F7 3F F7 3F 3F 6C 7F 3F 3F D7 E5 F0 F8 E9 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 6F 7D 3F B1 D2 F7 FF F1 F6 3F F7 53 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F AD 3F 3F A2 54 3F 3F 18 D8 6F 48 3F 14 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F B0 B7 B6 B7 B0 3F BC BC A2 A1 BC BF

Aber nach dem nochmaligen anwenden kommt

ÿØÿà..JFIF.....d.d..ÿì..Ducky..........ÿî.!Adobe.dÀ..............=..2Ç..‹KÿÛ.„......................................#$&$#.//22//
FF D8 FF E0 00 10 4A 46 49 46 00 01 02 00 00 64 00 64 00 00 FF EC 00 11 44 75 63 6B 79 00 01 00 04 00 00 00 1F 00 00 FF EE 00 21 41 64 6F 62 65 00 64 C0 00 00 00 01 03 00 10 03 02 03 06 00 00 14 3D 00 00 32 C7 00 00 8B 4B FF DB 00 84 00 10 0B 0B 0B 0C 0B 10 0C 0C 10 17 0F 0D 0F 17 1A 14 10 10 14 1A 1E 17 17 17 17 17 1E 1D 17 1A 19 19 1A 17 1D 1D 23 24 26 24 23 1D 2F 2F 32 32 2F 2F


heraus, was aber ungleich der Ausgangssituation ist.. :(
 
Ohne jetzt das Ganze genau durchzusehen, wäre meine Idee, dass Du die Binarydateien wie Textdateien behandelst, warum nicht einfach alle Dateien Byte für Byte lesen Byte für Byte verknüpfen und Byte für Byte schreiben, dann kann auch nichts schiefgehen.
Oder hab ich das jetzt beim überfliegen total falsch gesehen?

Gruß
Herbert
 
Kannst du mir dann bitte sagen, wo der Unterschied liegt. Lesen tue ich per
BufferedInputStream(FileInputStream)).read()
Dabei lese ich immer 32 Byte am Stück.

Beim umwandeln nehme ich jeweils einen char des Passwortes und des Textes, wandel sie in Binärzahlen um, vergleiche sie und füge jeweils eine 1 oder 0 an einen String. (Jaja, Strings kann man nicht verändern. Eben meine Ausdrucksweiße.)
Dann gehe ich nochmal durch und wandle sie zurück in Hexadezimale zahlen um.
Und dann schreibe ich sie.
 
Du scheinst ja noch etwas anderes als eine reine XOR Verknüpfung zu machen, denn sonst wäre die verschlüsselte Nachricht ja genauso lang wie die unverschlüsselte.
Ansonsten wäre mein Verdacht, dass beim lesen schreiben oder umwandeln der Binärdaten in Strings irgendein Steuerzeichen eingefügt wird , aber das ist zugegeben Spekulation meinerseits auch wenn mir irgendwie so etwas im Kopf herum spukt.

Auf jeden Fall ist deine Crypt-Klasse sehr lang wenn die tatsächlich nur eine XOR-Verknüpfung macht, denn dafür gibt es doch den ^ Operator der ein bitweises exklusives Oder durchführt.

Wie dem auch sei, bringt es dir nur Vorteile bei byte zu bleiben, neben übersichtlicherem Code garantiere ich dir auch einen enormen Performancegewinn, da sich das ganze Gecaste und Umgewandel dann darauf beschränkt einmal das Passwort in ein Bytearray zu wandeln :)
 
Danke für den Tip.
Ich habs jetzt so umgeschrieben:

Java:
static byte[] run(String block, Password password)
{
	password.setPasswordLenght(block.length());
	String pass = password.getPassword();
	
	byte[] result = new byte[block.length()];
	
	byte[] bytesOfBlock = block.getBytes();
	byte[] bytesOfPassword = pass.getBytes();
	
	for(int i = 0; i < bytesOfBlock.length; i++)
	{
		byte byteOfBlock = bytesOfBlock[i];
		byte byteOfPassword = bytesOfPassword[i];
		
		result[i] = (byte)(byteOfBlock^byteOfPassword);
	}
	return(result);
}

Funktioniert aber dennoch nicht. :-(
 
Wie sicher bist Du dir, dass wenn Du Binärdaten in Strings umwandelst und diese wieder zurück, dasselbe dabei rauskommt?

Man könnte sich die Stringklasse mal ganz genau anschauen oder einen Javaguru fragen, oder von vornherein bei Bytes bleiben ;)

mein nächster Schritt wäre der crypt-Funktion statt dem String block ein byte-Array zu übergeben und dementsprechend auch die Datei von vornherein byteweise zu lesen und natürlich hinterher ebenso zu schreiben. Auch das bringt übrigens wieder einen Performancegewinn. :)

Und wenn dann immer noch ein Fehler auftaucht, liegt das garantiert nicht an der Stringklasse sondern ganz sicher an deinem Code ;)
 
Hallo,

schau mal hier:
Java:
package de.tutorials.training;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

public class Crypt {

  /**
   * @param args
   */
  public static void main(String[] args) throws Exception {
    Crypt crypt = new Crypt();
    String encrypted = crypt.encrypt("www.tutorials.de", "hallo");
    System.out.println(encrypted);
    String decrypted = crypt.decrypt(encrypted, "hallo");
    System.out.println(decrypted);

    File encryptedFile = crypt.encrypt(new File("c:/Humpback_Whale.jpg"), "hallo");
    File decryptedFile = crypt.decrypt(encryptedFile, "hallo");
    System.out.println(decryptedFile);
  }


  File encrypt(File file, String password) throws Exception {
    File outputFile = new File(file.getAbsolutePath() + ".enc");
    return crypt(file, password, outputFile);
  }


  File decrypt(File file, String password) throws Exception {
    if (!file.getName().endsWith(".enc")) {
      return file;
    }
    String outFilePath = file.getAbsolutePath().replaceAll(".enc", ".dec");

    File outputFile = new File(outFilePath);
    return crypt(file, password, outputFile);
  }

  File crypt(File inputFile, String password, File outputFile) throws Exception {
    OutputStream outputStream = new FileOutputStream(outputFile);
    InputStream inputStream = new FileInputStream(inputFile);

    byte[] passwordBytes = password.getBytes("UTF-8");

    int bytesRead = -1;
    byte[] bytes = new byte[8192];
    while ((bytesRead = inputStream.read(bytes)) != -1) {
      outputStream.write(crypt(Arrays.copyOf(bytes, bytesRead), passwordBytes));
    }

    return outputFile;
  }

  String encrypt(String text, String password) throws Exception{
    return crypt(text, password);
  }


  String decrypt(String text, String password) throws Exception{
    return crypt(text, password);
  }


  String crypt(String text, String password) throws Exception{
    return new String(crypt(text.getBytes("UTF-8"), password.getBytes("UTF-8")));
  }

  byte[] crypt(byte[] input, byte[] password) throws Exception{
    byte[] output = new byte[input.length];

    for (int i = 0; i < output.length; i++) {
      output[i] = (byte) ((input[i] ^ password[i % password.length]) & 0xFF);
    }

    return output;
  }

}

Gruß Tom
 
Danke, aber es funktioniert noch immer nicht.
Ich habe das Projekt jetzt folgendermaßen umgebaut:

Java:
for(long l = 0; l < fileSize; l+=StartCrypt.BLOCK_LENGHT)
{
	byte[] read = new byte[StartCrypt.BLOCK_LENGHT];

	try
	{
		for(int i = 0; i < StartCrypt.BLOCK_LENGHT &&
		readFile.getStream().read(read) != -1; i++);
	}
	catch(Exception e)
	{
		e.printStackTrace();
	}
	
	byte[] cryptText = Crypt.run(read, pass);
	writeFile.write(cryptText);
}
Java:
	static byte[] run(byte[] block, Password password)
	{
		password.setPasswordLenght(block.length);
		String pass = password.getPassword();
		
		byte[] result = new byte[block.length];
		
		byte[] bytesOfBlock = null;
		byte[] bytesOfPassword = null;
		try
		{
			bytesOfBlock = String.valueOf(block).getBytes("UTF-8");
			bytesOfPassword = pass.getBytes("UTF-8");
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
		
		for(int i = 0; i < bytesOfBlock.length; i++)
		{
			result[i] = (byte) ((bytesOfBlock[i] ^
					bytesOfPassword[i % pass.length()]) & 0xFF);
		}
		return(result);
	}
 
Zurück