# AES Entschlüsselung teilweise fehlerhaft



## Enfreyer (15. Nov 2013)

Hallo Leute!

Ich bin gerade dabei, mir einen Passwortmanager zu bauen. Dazu erzeuge ich ein XML Document,
welches dann in einen String umgewandelt wird. Dieser String wird mit einem gehashten Passwort
verschlüsselt und dann (binär) in einer Datei abgelegt.

Soweit zur Theorie .


Das Problem ist: Wenn ich die Datei entschlüssele, sind manche der Blöcke fehlerhaft entschlüsselt,
andere aber nicht.

In das Praxis sieht das so aus:

*XML Document als String:*
converted document: <?xml version="1.0" encoding="UTF-8" standalone="no"?><Quatsch><account id="1"><Account>Typ</Account><Password>hardt</Password></account></Quatsch>

*Verschlüsselter XML String (Auszug):*
É0¦˜8ÒäPeó^ÌØæ:W*Ë8×¾?)k¿.)pŒÉn±R1N3™ØÚ2&Ëë/\Šßh†íÄX…J È$µÎ+%ÖJì ..... 

*Entschlüsselter XML String:*
<?xml version="1óÁ¥q«
:ø©xÓÄðP1F-8" standalone="no"?><Quatsch><account id="1"><Account>Typ</Account><Password>hardt</Password></account></Quatsch>

---

Da ich das ECB Chaining (vorerst) benutze, werden immer nur 16 Zeichen am Stück entschlüsselt.
Man erkennt hier, dass der zweite 16er Block falsch entschlüsselt wurde. Das treibt mich seit zwei 
Tagen in den Wahnsinn...


Die Methoden sehen folgendermaßen aus:

*Encryption + Save to File*

```
private static String encrypt(byte[] value, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, DecoderException, NoSuchProviderException {
		byte[] keyByteArray = org.apache.commons.codec.binary.Hex.decodeHex(key.toCharArray());
		SecretKeySpec skeySpec = new SecretKeySpec(keyByteArray, "AES");
		
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
		System.out.println(value.length);
		String encrypted = new String(cipher.doFinal(value));

		return encrypted;
	}



	public void writeToFile(String input) throws IOException, TransformerException {
		eraseFile();
		createFile();
	
		System.out.println("about to write1: " + input + "\n");
		
		FileOutputStream fos = new FileOutputStream(f);
		byte[] codeToWrite = input.getBytes();

		System.out.println("about to write len: " + codeToWrite.length + "\n");
		fos.write(codeToWrite);
		fos.close();

	}
```


*Decryption*

```
public String getDecrypted() throws Exception {
		String result = "";
		byte[] encrypted = getEncryptedFromFile();

		if (encrypted!=null) {
			
			result = decrypt(encrypted, password);
		} 
		return result;
	}

	
	private byte[] getEncryptedFromFile() throws IOException {
		System.out.println("the username is: " +username);
		byte[] result = fa.readFile();
		System.out.println("read file lenght is: " + result.length + "\n");
		return result;	
	}


public byte[] readFile() throws IOException {
		FileInputStream fin = new FileInputStream(f);
		byte fileContent[] = new byte[(int)f.length()];
		fin.read(fileContent);
		fin.close();
	    return fileContent;
	}


	private static String decrypt(byte[] value, String key) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, DecoderException, NoSuchProviderException {
		byte[] keyByteArray = org.apache.commons.codec.binary.Hex.decodeHex(key.toCharArray());
		SecretKeySpec skeySpec = new SecretKeySpec(keyByteArray, "AES");     
		
		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		System.out.println("Length to decrypt: " + value.length);
		String originalMessage = new String(cipher.doFinal(value));
		
		System.out.println("Decrypted 2: " + originalMessage );
		return originalMessage;
	}
```


Ich habe auch schon so eine Vermutung. Ich glaube, dass bei der Kodierung der Zeichenfolgen
beim Speichern oder beim Lesen etwas schief läuft; also dass das Encoding aus der Datei nicht
mehr richtig gelesen wird und dadurch einzelne Zeichen falsch interpretiert werden. Das würde
erklären, warum nur manche Blöcke falsch entschlüsselt werden.

Ich habe auch schon mit dem BASE64 Encoder der Apache Codecs herumexperimentiert und
binäre Zeichenfolge in die Datei geschrieben. Das war aber mehr oder weniger Trial and Error
und hat zu keinem Ergebnis geführt. Ganz im Gegenteil sogar.

Hat jemand eine Idee?


----------



## Fab1 (15. Nov 2013)

ich stand mal vor dem gleichen Problem. 

hab mir dann Methoden wie byteToString() stringToByte() geschrieben und immer nur diese Methoden verwendet um Strings "kurz" zu parsen. Hab den Code der Methoden leider nicht mehr.

Vielleicht schau ich später mal kurz über deinen Code drüber, momentan raucht mir aber noch zu sehr der Kopf.


----------



## Enfreyer (15. Nov 2013)

Das wäre nice!
Danke schonmal .


----------



## Fab1 (15. Nov 2013)

Noch eine Frage: Kannst du denn einen Satz mit Umlauten entschlüsseln und verschlüsseln, bei dem dann immer das gleiche rauskommt? Also hackt es wirklich nur am schreiben/auslesen aus der Datei?


Nun ich hab nicht direkt einen Fehler gefunden, aber du kannst wenn du Lust hast mal folgendes versuchen. Ich hab jetzt mal die Methoden wie oben beschrieben eingebaut.


```
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
     
     
    public class Test {
     
       
        public static void main(String[] args) throws UnsupportedEncodingException {
           
        	String plainString = "Kühe dürfen nicht schnell laufen, damit sie ihre Milch nicht verschütten.";
        	System.out.println(plainString);
        	byte[] b= CryptHelper.stringToByte(plainString);
        	System.out.println(CryptHelper.byteToString(b));
        }
        
        private static String encrypt(byte[] value, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, UnsupportedEncodingException {
    		byte[] keyByteArray = CryptHelper.stringToByte(key);
    		SecretKeySpec skeySpec = new SecretKeySpec(keyByteArray, "AES");
    		
    		
    		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
    		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    		System.out.println(value.length);
    		String encrypted = CryptHelper.byteToString(cipher.doFinal(value));

    		return encrypted;
    	}


    	private static String decrypt(byte[] value, String key) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, NoSuchProviderException, UnsupportedEncodingException {
    		byte[] keyByteArray = CryptHelper.stringToByte(key);
    		SecretKeySpec skeySpec = new SecretKeySpec(keyByteArray, "AES");     
    		
    		Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
    		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    		System.out.println("Length to decrypt: " + value.length);
    		String originalMessage = CryptHelper.byteToString(cipher.doFinal(value));
    		
    		System.out.println("Decrypted 2: " + originalMessage );
    		return originalMessage;
    	}
           
    }
```


```
import java.io.UnsupportedEncodingException;


public class CryptHelper {

	

	public static String byteToString(byte[] b) throws UnsupportedEncodingException{
		
		String result = new String(b, "UTF-8");
		
		return result;
		
	}
	
	public static byte[] stringToByte(String s) throws UnsupportedEncodingException{
		
		byte [] result = s.getBytes("UTF-8");
		return result;
		
	}

}
```

Lass dich nicht von der Einfachheit der Lösungsvorschlages beeinflussen, denn genau solch kleiner "Unachtsamkeiten" können den Fehler verursachen.

Ansonsten kann ich dir den Tipp geben. Immer Stück für Stück vorzugehen. Nicht immer gleiche alles schreiben und erst dann testen, dann ist die Fehlerreichweite im Code sehr groß.


----------



## Enfreyer (15. Nov 2013)

Da haste natürlich recht .

Herzlichen Dank schon mal.

Also, wenn ich das frisch verschlüsselte Ergebnis direkt wieder entschlüssel, dann kommt
das richtige raus. Es muss also beim Lesen oder Schreiben aus/in die Datei passieren. Allerdings
steht in der Datei haargenau das, was die encrypt-Methode erzeugt. Und nach dem Auslesen
der Datei habe ich auch exakt diesen String wieder.

Nur wenn ich das Ergebnis vom encrypt direkt ans decrypt übergebe, funzt es.

Auch mit deinen Methoden ist es leider nicht anders.

Ich probier mal noch ein wenig rum und melde mich in Kürze nochmal .


----------



## Enfreyer (16. Nov 2013)

So. Ich habe zwar nicht die leiseste Ahnung, woran es lag, aber ich habe jetzt eine Lösung.
Habe diesen Part nochmal als separates Programm geschrieben, getrennt von dem ganzen XML
Rattenschwanz und den anderen Methoden.

Funktionsweise:
Einen String mit einem beliebigen Passwort mit AES (CBC Chaining und Padding) verschlüsseln.
Verschlüsselten String in eine Datei schreiben.
Verschlüsselten String aus derselben Datei lesen.
String wieder entschlüsseln.


Für alle, die vor demselben Problem stehen, hier die Lösung:


```
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;

public class Main {
	static String passwordAsHash;
	
	static String rawString; // zu verschlüsselnder Text
	static byte[] rawByte;

	static String encryptedString; // verschlüsselter Text
	static byte[] encryptedByte; 
	
	static String decryptedString; // entschlüsslter Text
	static byte[] decryptedByte; 
	
	
	public static void main(String[] args) throws NoSuchAlgorithmException, IOException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, DecoderException, InvalidKeyException {
		passwordAsHash = getHashFromString("totalSicheresPasswort");

		rawString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><Quatsch><account id=\"1\"><Account>Typ</Account><Password>hardt</Password></account></Quatsch>";
		rawByte = rawString.getBytes("UTF-8");
		System.out.println("String before encrypting: " + rawString + "\n");
		
		encryptedByte = encrypt(rawByte);
		encryptedString = new String(encryptedByte, "UTF-8");
		System.out.println("String before writing: " + encryptedString + "\n");

		writeByteToFile(encryptedByte);
		
		encryptedByte = readByteFromFile();
		encryptedString = new String(encryptedByte, "UTF-8");
		System.out.println("String after reading: " + encryptedString + "\n");
		
		decryptedByte = decrypt(encryptedByte);
		decryptedString = new String(decryptedByte, "UTF-8");
		System.out.println("String after decryption: " + decryptedString + "\n");
	}

	private static byte[] encrypt(byte[] in) throws DecoderException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
		byte[] keyByteArray = org.apache.commons.codec.binary.Hex.decodeHex(passwordAsHash.toCharArray());
		SecretKeySpec skeySpec = new SecretKeySpec(keyByteArray, "AES");
		Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
		return cipher.doFinal(in);
	}
	
	private static byte[] decrypt(byte[] in) throws InvalidKeyException, DecoderException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
		byte[] keyByteArray = org.apache.commons.codec.binary.Hex.decodeHex(passwordAsHash.toCharArray());
		SecretKeySpec skeySpec = new SecretKeySpec(keyByteArray, "AES");
		Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		return cipher.doFinal(in);
	}
	
	private static byte[] readByteFromFile() throws IOException {
		File f = new File("theFile");
		FileInputStream is = new FileInputStream(f);
		byte[] result = new byte[(int)f.length()];
		is.read(result);
		is.close();
		return result;
	}

	private static void writeByteToFile(byte[] in) throws IOException {
		File f = new File("theFile");
		FileOutputStream out = new FileOutputStream(f);
		out.write(in);
		out.close();
	}
	
	private static String getHashFromString(String value) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		String result = "";
		MessageDigest dig = MessageDigest.getInstance("MD5");
		byte[] theHash = dig.digest(value.getBytes("UTF-8")); 
		StringBuilder sb = new StringBuilder(2*theHash.length); 
		for(byte b : theHash){ 
			sb.append(String.format("%02x", b&0xff));
		}
		result = sb.toString();
		return result;
	}
}
```


----------

