// other imports
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKey;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

final class Utils
{
	final static int MAX_FILE_SIZE = 5*1024*1024;

	public static byte[] fileToByteArray(String filename)
		throws Exception
	{
		File f = new File(filename);
		InputStream in =new FileInputStream(f);
		byte [] buffer = new byte[MAX_FILE_SIZE];
		
		int bytesRead = in.read(buffer);
		if (bytesRead == -1 || bytesRead > MAX_FILE_SIZE) {
			throw new Exception("File too large, currently limited to 5 megs");
		}
		return Arrays.copyOf(buffer, bytesRead);
	}
	
	public static void byteArrayToFile(byte[] content, String filename)
		throws IOException
	{
		System.out.println("Name:" + filename + " and size: " + content.length);
		OutputStream out = new FileOutputStream(filename);
		out.write(content);
		out.close();
	}
}

public class AesEncrypter
{

        boolean debug = false;

        private byte[] key;
        private byte[] iv;
        private int encryptionMode;
        private String paddingMode;
        
        static final int CBC_MODE = 0;
        static final int ECB_MODE = 1;

        static final String NO_PADDING = "NoPadding";
        static final String ZERO_PADDING = "ZeroPadding";
        static final String PKCS7_PADDING = "PKCS5Padding";

        protected byte[] encrypt(byte[] content) {

                byte[] cipherText = null;
                try {

                        IvParameterSpec ivSpec = new IvParameterSpec(iv);
                        SecretKey secretKey = new SecretKeySpec(key, "AES");
                        Cipher aes = null;
                        if (getEncryptionMode() == ECB_MODE) {
                                debug("Cipher mode: " + "AES/ECB/" + getPaddingMode());
                                aes = Cipher.getInstance("AES/ECB/" + getPaddingMode());
                                aes.init(Cipher.ENCRYPT_MODE, secretKey);
                        } else {
                                debug("Cipher mode: " + "AES/CBC/" + getPaddingMode());
                                aes = Cipher.getInstance("AES/CBC/" + getPaddingMode());
                                aes.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
                        }
                        cipherText = aes.doFinal(content);

                 } catch (Exception e) {
                        debug("Error in encryption:", e);
                }
                return cipherText;

        }

        protected byte[] decrypt(byte[] content)
        {

                byte[] plainText = null;
                try {

                        IvParameterSpec ivSpec = new IvParameterSpec(iv);
                        SecretKey secretKey = new SecretKeySpec(key, "AES");
                        Cipher aes = null;
                        if (getEncryptionMode() == ECB_MODE) {
                                aes = Cipher.getInstance("AES/ECB/" + paddingMode);
                                aes.init(Cipher.DECRYPT_MODE, secretKey);
                        } else {
                                aes = Cipher.getInstance("AES/CBC/" + paddingMode);
                                aes.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
                        }
                        plainText = aes.doFinal(content);

                } catch (Exception e) {
                        debug("Error in decryption:", e);
                }
                return plainText;
        }
        
        public static String bytes2Hex(byte[] bytes)
        {

                if (bytes == null)
                        return null;

                StringBuffer b = new StringBuffer();
                for(int i=0; i<bytes.length; i++) {
                        b.append(String.format("%1$02x",bytes[i]));
                }
                return b.toString();
        }

        public static byte[] hex2Bytes(String hex)
        {
                int len = hex.length();
                if (len % 2 == 1)
                        return null;

                System.out.println("Bytes:" + len);
                byte[] b = new byte[len/2];
                for (int i=0; i<len; i += 2) {
                        b[i>>1] = (byte)Integer.parseInt(hex.substring(i,i+2),16);
                }

                return b;
        }

        public void debug(String s, Throwable t)
        {
        	System.out.println(s + t.getMessage());
        }
        
        public void debug(String s)
        {
        	System.out.println(s);
        }
        
    	public void setIv(byte[] iv)
    	{
			this.iv = iv;
		}

		public byte[] getIv()
		{
			return iv;
		}
		
        public void setKey(byte[] key)
        {
			this.key = key;
		}

		public byte[] getKey()
		{
			return key;
		}
        
		private void setEncryptionMode(int encryptionMode)
		{
			this.encryptionMode = encryptionMode;
		}

		private int getEncryptionMode()
		{
			return encryptionMode;
		}

		private void setPaddingMode(String paddingMode)
		{
			this.paddingMode = paddingMode;
		}

		private String getPaddingMode()
		{
			return paddingMode;
		}
		
		public static void main(String[] args)
        {
        	if (args.length <4) {
        		System.out.println("Usage: AesEncrypter key iv infile outfile");
        		return;
        	}
        	AesEncrypter aes = new AesEncrypter();
        	System.out.println("key:" + args[0]);
        	aes.setKey(AesEncrypter.hex2Bytes(args[0]));
        	System.out.println("iv:" + args[1]);
        	aes.setIv(AesEncrypter.hex2Bytes(args[1]));
        	aes.setEncryptionMode(CBC_MODE);
        	aes.setPaddingMode(PKCS7_PADDING);
        	
        	try {
        		byte[] encrypted = aes.encrypt(Utils.fileToByteArray(args[2]));
        		Utils.byteArrayToFile(encrypted, args[3]);
        		
        	} catch (Exception e) {
        		System.out.println("Failed to encrypt file: " + e.getMessage());
        	}
        }
}
