content: package xml;
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.security.InvalidKeyException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.security.spec.InvalidKeySpecException;
  import java.security.spec.KeySpec;
  
  import javax.crypto.BadPaddingException;
  import javax.crypto.Cipher;
  import javax.crypto.IllegalBlockSizeException;
  import javax.crypto.NoSuchPaddingException;
  import javax.crypto.SecretKey;
  import javax.crypto.SecretKeyFactory;
  import javax.crypto.spec.PBEKeySpec;
  import javax.crypto.spec.SecretKeySpec;
  
  /**
   * Hilfsklasse mit statischen Methoden zum Ver- und Entschlsseln von Daten,
   * wird von {@link XMLTools} verwendet.
   * @author Alexander Herzog
   * @see XMLTools
   */
  public class ChiperTools {
  	/**
  	 * Konstruktor der Klasse<br>
  	 * Die Klasse kann nicht instanziert werden;
  	 * sie stellt nur statische Hilfsfunktionen zur Verfgung.
  	 */
  	private ChiperTools() {}
  
  	/**
  	 * Erstellt ein {@link Cipher}-Objekt zum Ver- oder Entschlsseln von Daten
  	 * @param password	Passwort
  	 * @param mode	Ver- oder Entschlsselungsmodus ({@link Cipher#ENCRYPT_MODE} oder {@link Cipher#DECRYPT_MODE})
  	 * @return	Liefert im Erfolgsfall das {@link Cipher}-Objekt, sonst <code>null</code>.
  	 */
  	private static final Cipher getChiper(String password, int mode) {
  		SecretKeyFactory factory;
  		try {
  			factory=SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
  		} catch (NoSuchAlgorithmException e1) {return null;}
  		KeySpec spec=new PBEKeySpec(password.toCharArray(),"nosalt".getBytes(),256,128);
  		SecretKey tmp;
  		SecretKey secret=null;
  		try {
  			tmp=factory.generateSecret(spec);
  			secret=new SecretKeySpec(tmp.getEncoded(),"AES");
  		} catch (InvalidKeySpecException e1) {return null;}
  
  		Cipher cipher;
  		try {
  			cipher=Cipher.getInstance("AES");
  		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {return null;}
  
  		try {
  			cipher.init(mode,secret);
  		} catch (InvalidKeyException e) {return null;}
  
  		return cipher;
  	}
  
  	/**
  	 * Berechnet eine SHA-Prfsumme ber eine Byte-Sequenz
  	 * @param b	Byte-Sequenz ber die die Prfsumme berechnet werden soll
  	 * @return	Prfsumme
  	 */
  	private static final byte[] getChecksum(byte[] b) {
  		MessageDigest md;
  		try {
  			md=MessageDigest.getInstance("SHA");
  		} catch (NoSuchAlgorithmException e) {return null;}
  
  		md.update(b);
  		byte[] sha=md.digest();
  		if (sha.length!=20) return null;
  		return sha;
  	}
  
  	/**
  	 * Verschlsselt Daten mit AES
  	 * @param data	Zu verschlsselnde Daten
  	 * @param password	Passwort
  	 * @return	Verschlsselte Daten (oder im Fehlerfall <code>null</code>)
  	 * @see #decrypt(ByteArrayOutputStream, String)
  	 */
  	public static final ByteArrayOutputStream encrypt(final ByteArrayOutputStream data, final String password) {
  		final ByteArrayOutputStream data2=new ByteArrayOutputStream();
  
  		/* Prfsumme berechnen und eigentlichen Daten voran stellen */
  		byte[] sha=getChecksum(data.toByteArray()); if (sha==null) return null;
  		try {
  			data2.write(sha);
  			data2.write(data.toByteArray());
  		} catch (IOException e1) {return null;}
  
  		/* Daten verschlsseln */
  		Cipher cipher=getChiper(password,Cipher.ENCRYPT_MODE);
  		if (cipher==null) return null;
  
  		ByteArrayOutputStream output=new ByteArrayOutputStream();
  		try {
  			byte[] b=cipher.doFinal(data2.toByteArray());
  			output.write(b);
  		} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {return null;}
  
  		return output;
  	}
  
  	/**
  	 * Verschlsselt Daten mit AES
  	 * @param data	Zu verschlsselnde Daten
  	 * @param password	Passwort
  	 * @return	Verschlsselte Daten (oder im Fehlerfall <code>null</code>)
  	 * @see #decrypt(byte[], String)
  	 */
  	public static final byte[] encrypt(final byte[] data, final String password) {
  		final ByteArrayOutputStream data2=new ByteArrayOutputStream();
  
  		/* Prfsumme berechnen und eigentlichen Daten voran stellen */
  		byte[] sha=getChecksum(data); if (sha==null) return null;
  		try {
  			data2.write(sha);
  			data2.write(data);
  		} catch (IOException e1) {return null;}
  
  		/* Daten verschlsseln */
  		final Cipher cipher=getChiper(password,Cipher.ENCRYPT_MODE);
  		if (cipher==null) return null;
  
  		ByteArrayOutputStream output=new ByteArrayOutputStream();
  		try {
  			byte[] b=cipher.doFinal(data2.toByteArray());
  			output.write(b);
  		} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {return null;}
  
  		return output.toByteArray();
  	}
  
  	/**
  	 * Entschlsselt Daten mit AES
  	 * @param data	Zu entschlsselnde Daten
  	 * @param password	Passwort
  	 * @return	Entschlsselte Daten (oder im Fehlerfall <code>null</code>)
  	 * @see #encrypt(ByteArrayOutputStream, String)
  	 */
  	public static final ByteArrayOutputStream decrypt(final ByteArrayOutputStream data, final String password) {
  		/* Daten entschlsseln */
  		final Cipher cipher=getChiper(password,Cipher.DECRYPT_MODE);
  		if (cipher==null) return null;
  
  		final ByteArrayInputStream input;
  		try {
  			input=new ByteArrayInputStream(cipher.doFinal(data.toByteArray()));
  			input.reset();
  		} catch (IllegalBlockSizeException | BadPaddingException e) {return null;}
  
  		/* Prfsumme und Daten trennen */
  		if (input.available()<20) {return null;}
  		byte[] sha_orig=new byte[20];
  		input.read(sha_orig,0,20);
  		byte[] b=new byte[input.available()];
  		try {
  			if (input.read(b)<b.length) return null;
  		} catch (IOException e) {return null;}
  		byte[] sha_msg=getChecksum(b); if (sha_msg==null) {return null;}
  		for (int i=0;i<sha_orig.length;i++) if (sha_orig[i]!=sha_msg[i]) {return null;}
  
  		ByteArrayOutputStream output=new ByteArrayOutputStream();
  		try {
  			output.write(b);
  		} catch (IOException e) {return null;}
  		return output;
  	}
  
  	/**
  	 * Entschlsselt Daten mit AES
  	 * @param data	Zu entschlsselnde Daten
  	 * @param password	Passwort
  	 * @return	Entschlsselte Daten (oder im Fehlerfall <code>null</code>)
  	 * @see #encrypt(byte[], String)
  	 */
  	public static final byte[] decrypt(final byte[] data, final String password) {
  		/* Daten entschlsseln */
  		final Cipher cipher=getChiper(password,Cipher.DECRYPT_MODE);
  		if (cipher==null) return null;
  
  		final ByteArrayInputStream input;
  		try {
  			input=new ByteArrayInputStream(cipher.doFinal(data));
  			input.reset();
  		} catch (IllegalBlockSizeException | BadPaddingException e) {return null;}
  
  		/* Prfsumme und Daten trennen */
  		if (input.available()<20) {return null;}
  		byte[] sha_orig=new byte[20];
  		input.read(sha_orig,0,20);
  		byte[] b=new byte[input.available()];
  		try {
  			if (input.read(b)<b.length) return null;
  		} catch (IOException e) {return null;}
  		byte[] sha_msg=getChecksum(b); if (sha_msg==null) {return null;}
  		for (int i=0;i<sha_orig.length;i++) if (sha_orig[i]!=sha_msg[i]) {return null;}
  
  		ByteArrayOutputStream output=new ByteArrayOutputStream();
  		try {
  			output.write(b);
  		} catch (IOException e) {return null;}
  		return output.toByteArray();
  	}
  }