#### AES (Rijndael) Implementation issues in JAVA

Hey guys. Im new here so howdy! I'm currently writing my own implementation of the Rijndael cipher, or as we know it today the "advanced encryption standard". I'm having some troubles though, and I can't seem to isolate where in my code i'm going wrong. The implementation is in Java, and I really don't want to have to explain every detail of the algorithm, so can someone who knows it / already has implemented it have a go at answering my questions! Here is the output from a test class:

Plaintext : SixteenLettersssPlusFourMoreFour: Length : 32
4¤“-Ú!Ï «: CÖ6 : Length : 32
----------------------------
Deciphered Text: qÇ÷—S× OãËçXà”ÊáF*¯ù´m•Ë+ª«i : Length: 32

The output of the decryption is nothing like the plaintext! I dont know what is wrong with my code, and I know it's not the best way of doing this but I'll C+P my sourcecode for the class below. I apologize if this ruins anyone learning experience, I wouldn't meither you guys if I couldn't figure out what was wrong myself!

Code:
```
/*
* TODO: Problems with algorithm. Debugging stages.
* 		 Need to decide upon a worthwhile padding scheme - still TODO. Can wait until i'm done debugging.
*/

import java.util.ArrayList;
public class AESImplementation {
/*	 										##################################
* 											#		AES IMPLEMENTATION 		 #
* 											#			CHRIS COONEY		 #
* 											#				JAVA			 #
* 											##################################
*
* 											AES IMPLEMENTATION BY CHRIS COONEY
*
* 									This class encrypts into a custom AES encryption, and returns
* 								     ciphertext result as a string, taking input of string in the
* 								"encrypt()" method and an array of Integers as the initial Key or "IV"
*
* 											Copyright Free. This source code is in
* 										the public domain to use as the public wishes.
*
* 								This code comes with no warranty whatsoever. Use at your own risk.
*/

/*
*  Constant to determine size of Encryption Key, for 128 bit encryption.
*/
private final int n = 16;

/*
*  Encryption Key provided by the user.
*/
private int[] encKey;

/*
*  Expanded Keys.
*/

private String[][] keys;

/*
* list to hold plaintext blocks.
*/

private ArrayList<String[][]> blocks;

/*
*  Rijndael's S Box. Used as part of AES.
*/
private int[][] sBox = {

{ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76  } ,
{ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0  } ,
{ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15  } ,
{ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75  } ,
{ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84  } ,
{ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF  } ,
{ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8  } ,
{ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2  } ,
{ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73  } ,
{ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB  } ,
{ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79  } ,
{ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08  } ,
{ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A  } ,
{ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E  } ,
{ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF  } ,
{ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16  }
};

/*
* Inverse sBox. Used for Decryption
*/

private int[][]invSbox = {

{ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB } ,
{ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB } ,
{ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E } ,
{ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25 } ,
{ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92 } ,
{ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84 } ,
{ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06 } ,
{ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B } ,
{ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73 } ,
{ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E } ,
{ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B } ,
{ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4 } ,
{ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F } ,
{ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF } ,
{ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61 } ,
{ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D }
};
/*
*  rCon table, for performing rCon Calculatory look-ups.
*/
private int[] rCon = {

0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40,
0x80, 0x1b, 0x36, 0x6c,
0xd8, 0xab, 0x4d, 0x9a
};

/*
* Lookup table for multiplying by 2 in Galois' finite field.
*/
private int[] galoisMul2 = {

0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
};

/*
* Lookup Table for multiplying by 9 in Galois' finite field.
*/

private int[] galoisMul9 = {

0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
};

/*
*  Lookup table for multiplying by 11 in Galois' finite field.
*/
private int[] galoisMul11 = {

0x00,0x0b,0x16,0x1d,0x2c,0x27,0x3a,0x31,0x58,0x53,0x4e,0x45,0x74,0x7f,0x62,0x69,
0x7b,0x70,0x6d,0x66,0x57,0x5c,0x41,0x4a,0x23,0x28,0x35,0x3e,0x0f,0x04,0x19,0x12,
0xcb,0xc0,0xdd,0xd6,0xe7,0xec,0xf1,0xfa,0x93,0x98,0x85,0x8e,0xbf,0xb4,0xa9,0xa2,
0xf6,0xfd,0xe0,0xeb,0xda,0xd1,0xcc,0xc7,0xae,0xa5,0xb8,0xb3,0x82,0x89,0x94,0x9f,
0x46,0x4d,0x50,0x5b,0x6a,0x61,0x7c,0x77,0x1e,0x15,0x08,0x03,0x32,0x39,0x24,0x2f,
0x8d,0x86,0x9b,0x90,0xa1,0xaa,0xb7,0xbc,0xd5,0xde,0xc3,0xc8,0xf9,0xf2,0xef,0xe4,
0x3d,0x36,0x2b,0x20,0x11,0x1a,0x07,0x0c,0x65,0x6e,0x73,0x78,0x49,0x42,0x5f,0x54,
0xf7,0xfc,0xe1,0xea,0xdb,0xd0,0xcd,0xc6,0xaf,0xa4,0xb9,0xb2,0x83,0x88,0x95,0x9e,
0x47,0x4c,0x51,0x5a,0x6b,0x60,0x7d,0x76,0x1f,0x14,0x09,0x02,0x33,0x38,0x25,0x2e,
0x8c,0x87,0x9a,0x91,0xa0,0xab,0xb6,0xbd,0xd4,0xdf,0xc2,0xc9,0xf8,0xf3,0xee,0xe5,
0x3c,0x37,0x2a,0x21,0x10,0x1b,0x06,0x0d,0x64,0x6f,0x72,0x79,0x48,0x43,0x5e,0x55,
0x01,0x0a,0x17,0x1c,0x2d,0x26,0x3b,0x30,0x59,0x52,0x4f,0x44,0x75,0x7e,0x63,0x68,
0xb1,0xba,0xa7,0xac,0x9d,0x96,0x8b,0x80,0xe9,0xe2,0xff,0xf4,0xc5,0xce,0xd3,0xd8,
0x7a,0x71,0x6c,0x67,0x56,0x5d,0x40,0x4b,0x22,0x29,0x34,0x3f,0x0e,0x05,0x18,0x13,
0xca,0xc1,0xdc,0xd7,0xe6,0xed,0xf0,0xfb,0x92,0x99,0x84,0x8f,0xbe,0xb5,0xa8,0xa3
};

/*
*  Lookup table for multiplying by 13 in Galois' finite field.
*/
private int[] galoisMul13 = {

0x00,0x0d,0x1a,0x17,0x34,0x39,0x2e,0x23,0x68,0x65,0x72,0x7f,0x5c,0x51,0x46,0x4b,
0xd0,0xdd,0xca,0xc7,0xe4,0xe9,0xfe,0xf3,0xb8,0xb5,0xa2,0xaf,0x8c,0x81,0x96,0x9b,
0xbb,0xb6,0xa1,0xac,0x8f,0x82,0x95,0x98,0xd3,0xde,0xc9,0xc4,0xe7,0xea,0xfd,0xf0,
0x6b,0x66,0x71,0x7c,0x5f,0x52,0x45,0x48,0x03,0x0e,0x19,0x14,0x37,0x3a,0x2d,0x20,
0x6d,0x60,0x77,0x7a,0x59,0x54,0x43,0x4e,0x05,0x08,0x1f,0x12,0x31,0x3c,0x2b,0x26,
0xbd,0xb0,0xa7,0xaa,0x89,0x84,0x93,0x9e,0xd5,0xd8,0xcf,0xc2,0xe1,0xec,0xfb,0xf6,
0xd6,0xdb,0xcc,0xc1,0xe2,0xef,0xf8,0xf5,0xbe,0xb3,0xa4,0xa9,0x8a,0x87,0x90,0x9d,
0x06,0x0b,0x1c,0x11,0x32,0x3f,0x28,0x25,0x6e,0x63,0x74,0x79,0x5a,0x57,0x40,0x4d,
0xda,0xd7,0xc0,0xcd,0xee,0xe3,0xf4,0xf9,0xb2,0xbf,0xa8,0xa5,0x86,0x8b,0x9c,0x91,
0x0a,0x07,0x10,0x1d,0x3e,0x33,0x24,0x29,0x62,0x6f,0x78,0x75,0x56,0x5b,0x4c,0x41,
0x61,0x6c,0x7b,0x76,0x55,0x58,0x4f,0x42,0x09,0x04,0x13,0x1e,0x3d,0x30,0x27,0x2a,
0xb1,0xbc,0xab,0xa6,0x85,0x88,0x9f,0x92,0xd9,0xd4,0xc3,0xce,0xed,0xe0,0xf7,0xfa,
0x67,0x6a,0x7d,0x70,0x53,0x5e,0x49,0x44,0x0f,0x02,0x15,0x18,0x3b,0x36,0x21,0x2c,
0x0c,0x01,0x16,0x1b,0x38,0x35,0x22,0x2f,0x64,0x69,0x7e,0x73,0x50,0x5d,0x4a,0x47,
0xdc,0xd1,0xc6,0xcb,0xe8,0xe5,0xf2,0xff,0xb4,0xb9,0xae,0xa3,0x80,0x8d,0x9a,0x97
};

/*
*  Lookup table for multiplying by 14 in Galois' finite field.
*/
private int[] galoisMul14 = {

0x00,0x0e,0x1c,0x12,0x38,0x36,0x24,0x2a,0x70,0x7e,0x6c,0x62,0x48,0x46,0x54,0x5a,
0xe0,0xee,0xfc,0xf2,0xd8,0xd6,0xc4,0xca,0x90,0x9e,0x8c,0x82,0xa8,0xa6,0xb4,0xba,
0xdb,0xd5,0xc7,0xc9,0xe3,0xed,0xff,0xf1,0xab,0xa5,0xb7,0xb9,0x93,0x9d,0x8f,0x81,
0x3b,0x35,0x27,0x29,0x03,0x0d,0x1f,0x11,0x4b,0x45,0x57,0x59,0x73,0x7d,0x6f,0x61,
0x4d,0x43,0x51,0x5f,0x75,0x7b,0x69,0x67,0x3d,0x33,0x21,0x2f,0x05,0x0b,0x19,0x17,
0x76,0x78,0x6a,0x64,0x4e,0x40,0x52,0x5c,0x06,0x08,0x1a,0x14,0x3e,0x30,0x22,0x2c,
0x96,0x98,0x8a,0x84,0xae,0xa0,0xb2,0xbc,0xe6,0xe8,0xfa,0xf4,0xde,0xd0,0xc2,0xcc,
0x41,0x4f,0x5d,0x53,0x79,0x77,0x65,0x6b,0x31,0x3f,0x2d,0x23,0x09,0x07,0x15,0x1b,
0xa1,0xaf,0xbd,0xb3,0x99,0x97,0x85,0x8b,0xd1,0xdf,0xcd,0xc3,0xe9,0xe7,0xf5,0xfb,
0x9a,0x94,0x86,0x88,0xa2,0xac,0xbe,0xb0,0xea,0xe4,0xf6,0xf8,0xd2,0xdc,0xce,0xc0,
0x7a,0x74,0x66,0x68,0x42,0x4c,0x5e,0x50,0x0a,0x04,0x16,0x18,0x32,0x3c,0x2e,0x20,
0xec,0xe2,0xf0,0xfe,0xd4,0xda,0xc8,0xc6,0x9c,0x92,0x80,0x8e,0xa4,0xaa,0xb8,0xb6,
0x0c,0x02,0x10,0x1e,0x34,0x3a,0x28,0x26,0x7c,0x72,0x60,0x6e,0x44,0x4a,0x58,0x56,
0x37,0x39,0x2b,0x25,0x0f,0x01,0x13,0x1d,0x47,0x49,0x5b,0x55,0x7f,0x71,0x63,0x6d,
0xd7,0xd9,0xcb,0xc5,0xef,0xe1,0xf3,0xfd,0xa7,0xa9,0xbb,0xb5,0x9f,0x91,0x83,0x8d
};

/*
*  Matrix used in the mixColumns transformation.
*/
private int[][] columnMatrix = {

{2, 3 ,1 ,1},
{1, 2, 3, 1},
{1, 1, 2, 3},
{3, 1, 1, 2}
};

/*
* Matrix used in inverse Mix Column's transformation.
*/
private int[][] invColumnMatrix = {

{14, 11, 13, 9},
{9, 14, 11, 13},
{13, 9, 14, 11},
{11, 13, 9, 14}
};

/*
* Constructor
*
* @param encryptionKey
*/

public AESImplementation(int[] encryptionKey)
{
encKey = encryptionKey;
keys = keyExpansion();
blocks = new ArrayList<String[][]>();
}
/*
* subBytes transformation
*
* @param plaintext
*/
private String[][] subBytes(String[][] plaintext)
{
// TESTING: Works fine.. Can't be the source of the bad values.
String temp = "";
String row = "";
String col = "";

for(int c = 0; c < 4; c++)
{
for(int d = 0; d < 4; d++)
{
temp = plaintext[c][d];
if(temp.length() == 2)
{
col = String.valueOf(temp.charAt(1));
row = String.valueOf(temp.charAt(0));

plaintext[c][d] = Integer.toHexString(sBox[Integer.parseInt(col,n)][Integer.parseInt(row, n)]);

}
else
{
col = String.valueOf(temp.charAt(0));
plaintext[c][d] = Integer.toHexString(sBox[Integer.parseInt(col,n)][0]);
}
}
}
return plaintext;
}
/*
* mixColumns transformation.
*
* @param plaintext
*/
private String[][] mixColumns(String[][] plaintext)
{
/*
* TESTING: Mathematically correct for all sample values. Have no idea what I'm doing wrong here..
*/

String temp, temp2 = "";
int matrixVal;
for(int x = 0; x < 4; x++)
{
for(int y = 0; y < 4; y++)
{
matrixVal = columnMatrix[x][y];
temp = plaintext[x][y];
if(matrixVal == 2)
{
plaintext[x][y] = Integer.toHexString(galoisMultiply(temp, 2));
}
else if(matrixVal == 3)
{
temp2 = Integer.toHexString(galoisMultiply(temp, 2));
plaintext[x][y] = Integer.toHexString(Integer.parseInt(temp2,n) ^ Integer.parseInt(temp,n));
}
}
}
return plaintext;
}
/*
* shiftRows Transformation
*
* @param plaintext.
*/

private String[][] shiftRows(String[][] plaintext)
{
// TESTING RESULTS: Works perfectly. Not the cause of bad values.
String[] temp = new String[4];

for(int x=1; x < 4; x++)
{
for(int g = 0; g < 4; g++)
{
temp[g] = plaintext[x][g];

}

temp = cycle(temp, x);

for(int y = 0; y < 4; y++)
{
plaintext[x][y] = temp[y];
}
}

return plaintext;
}

/*
*
* @param plaintext
* @param blockIndex
*/

private String[][] addRoundKey(String[][] plaintext, int blockIndex)
{
// TESTING: Seems to work perfectly. Isn't the cause of bad values.
int plaintextIndex = -1;

int blockIndexEnd = blockIndex + 4;

for(int blockIndexStart = blockIndex; blockIndexStart < blockIndexEnd; blockIndexStart++)
{
plaintextIndex ++;

for(int y = 0; y < 4; y++)
{
int temp1 = Integer.parseInt(keys[blockIndexStart][y],n);

int temp2 = Integer.parseInt(plaintext[plaintextIndex][y],n);

plaintext[plaintextIndex][y] = Integer.toHexString(temp1 ^ temp2);
}
}
return plaintext;
}

private String[][] keyExpansion()
{
//TESTING: Completely rewritten and should work, but can never be sure. Still candidate for bad value problem.

// Expands the Encryption key according to the Rijndael Key Schedule.

String[][] output = convertToTwoD(hexVal(encKey));

// Convert encryption key into a multidimensional array.

if(output != null)
{
String[][] expandedKeys = new String[45][4];
// Initialise the expandedKeys array.
for(int c = 0; c < 4;  c++)
{
for(int d = 0; d < 4; d++)
{
expandedKeys[c][d] = output[c][d];
// Place the original encryption key in the expanded keys.
}
}

int rConPoint=0;
int nextBlock = 3;
for(int counter = 3; counter < 44; counter ++)
{
for(int j=0; j < 4; j++)
{
if(counter == nextBlock)
{
rConPoint ++;
nextBlock += 4;
boolean rConUsed = false;

String[] nextRot = { expandedKeys[counter][0], expandedKeys[counter][1], expandedKeys[counter][2],expandedKeys[counter][3] };

nextRot = cycle(nextRot, 1);

nextRot = subBytes(nextRot);

String[] colMinus4 = { expandedKeys[counter-3][0], expandedKeys[counter-3][1], expandedKeys[counter-3][2],expandedKeys[counter-3][3] };

int rConVal = rCon[rConPoint];

for(int x = 0; x < 4; x++)
{
if(rConUsed == true)
{
rConVal = 0;
}
nextRot[x] = Integer.toHexString(Integer.parseInt(nextRot[x],16) ^ Integer.parseInt(colMinus4[x],16) ^ rConVal);

rConUsed = true;
}

for(int x = 0; x < 4; x++)
{
expandedKeys[counter+1][x] = nextRot[x];
}

break;
}
else
{

int val1 = Integer.parseInt(expandedKeys[counter - 3][j],n);

int val2 = Integer.parseInt(expandedKeys[counter][j],n);

expandedKeys[counter + 1][j] = Integer.toHexString(val1 ^ val2);
}
}
}

return expandedKeys;
}
else
{
// If the conversion to a 2d array returns a null.
return null;
}
}

private String[] hexVal(int[] input)
{

String[] output = new String[input.length];
for(int x = 0; x < input.length; x++)
{
output[x] = Integer.toHexString(input[x]);
}
return output;
}

private String[][] convertToTwoD(String[] input)
{
if(input.length == n)
{
String[][] output = new String[4][4];
output[0][0] = input[0];
output[0][1] = input[1];
output[0][2] = input[2];
output[0][3] = input[3];
output[1][0] = input[4];
output[1][1] = input[5];
output[1][2] = input[6];
output[1][3] = input[7];
output[2][0] = input[8];
output[2][1] = input[9];
output[2][2] = input[10];
output[2][3] = input[11];
output[3][0] = input[12];
output[3][1] = input[13];
output[3][2] = input[14];
output[3][3] = input[15];
return output;
}
else
{
return null;
}
}

private String[] cycle(String[] input, int iterations)
{
String temp;
for(int i = 1; i <= iterations; i++)
{
// Cycle the values around for the specified number of times.
temp = input[0];
input[0] = input[1];
input[1] = input[2];
input[2] = input[3];
input[3] = temp;
}

return input;
}

private String[] subBytes(String[] valsToSub)
{
// Replaces all values with value in Rijndael's S box.
@SuppressWarnings("unused")
String row = "";
@SuppressWarnings("unused")
String col = "";
String temp;

for(int c = 0; c < valsToSub.length; c++)
{
temp = valsToSub[c];

if(temp.length() == 2)
{
col = String.valueOf(temp.charAt(1));
row = String.valueOf(temp.charAt(0));
valsToSub[c] = Integer.toHexString(sBox[Integer.parseInt(col,16)][Integer.parseInt(row,16)]);
}
else
{
col = String.valueOf(temp.charAt(0));
valsToSub[c] = Integer.toHexString(sBox[Integer.parseInt(col,16)][0]);

}
}
return valsToSub;
}

private void formatPlaintext(String plaintext)
{
// Arranges String of plaintext into two dimensional arrays (blocks) of 4x4.
String temp = plaintext;
if(temp.length() > n)
{
storeBlocks(temp.substring(0,n));
formatPlaintext(temp.substring(n,temp.length()));
}
else if(temp.length() == n)
{
storeBlocks(temp);
}
else if(temp.length() < n)
{
storeBlocks(temp);
}
}

{
// Temporary padding. Make sure you fix this when it works.
{
}
}

private void storeBlocks(String blockStr)
{
String[][] block = new String[4][4];
int p = 0;
for(int x = 0; x < 4; x++)
{
for(int y = 0; y < 4; y++)
{
block[x][y] = Integer.toHexString((int)blockStr.charAt(p));
if(p < 14)
{
p++;
}
}
}
}

public String encrypt(String plaintext)
{
formatPlaintext(plaintext);
int block = 0;
String ciphertext = "";
for(String[][] str : blocks)
{
str = addRoundKey(str, block * 4);

block ++;

for(int x = 1; x <=9; x++)
{

str = subBytes(str);

str = shiftRows(str);

str = mixColumns(str);

str = addRoundKey(str, block * 4);

block ++;
}

str = subBytes(str);

str = shiftRows(str);

str = addRoundKey(str, block * 4);

block = 0;

ciphertext  = ciphertext + convertToString(str);
}

cleanUp();

return ciphertext;
}

private int galoisMultiply(String value, int factor)
{
if(factor == 2)
{
return galoisMul2[Integer.parseInt(value,16)];

}
else if(factor == 9)
{
return galoisMul9[Integer.parseInt(value,16)];

}
else if(factor == 11)
{
return galoisMul11[Integer.parseInt(value,16)];
}
else if(factor == 13)
{
return galoisMul13[Integer.parseInt(value,16)];
}
else
{
return galoisMul14[Integer.parseInt(value,16)];
}
}

private String convertToString(String[][] input)
{
String output = "";
for(int x = 0; x < 4; x++)
{
for(int y = 0; y < 4; y++)
{
output = output + (char)Integer.parseInt(input[x][y],16);
}
}
return output;
}

private void cleanUp()
{
blocks.clear();

for(int x = 0; x < 4; x++)
{
for(int y = 0; y < 4; y++)
{
keys[x][y] = "0x00";
}
}
}

public String decrypt(String ciphertext)
{
int block = 0;

String plaintext = "";

keys = keyExpansion();

formatPlaintext(ciphertext);

for(String[][] str : blocks)
{
str = addRoundKey(str, block * 4);

block ++;

for(int x = 1; x <=9; x++)
{

str = invShiftRows(str);

str = invSubBytes(str);

str = addRoundKey(str, block * 4);

str = invMixColumns(str);

block ++;
}

str = invShiftRows(str);

str = invSubBytes(str);

str = addRoundKey(str, block * 4);

block = 0;

plaintext = plaintext + convertToString(str);
}

cleanUp();

return plaintext;
}

private String[][] invShiftRows(String[][] plaintext)
{
// Shifts rows around the opposite direction to the shiftRows() transformation.
String[] temp = new String[4];

for(int x=1; x < 4; x++)
{
for(int g = 0; g < 4; g++)
{
temp[g] = plaintext[x][g];
}

temp = invCycle(temp, x);

for(int y = 0; y < 4; y++)
{
plaintext[x][y] = temp[y];
}
}

return plaintext;
}

private String[] invCycle(String[] input, int iterations)
{
// Inverse of the "cycle()" function. Rotates bytes to the right rather than left.
String temp;
for(int i = 1; i <= iterations; i++)
{
// Cycle the values around for the specified number of times.
temp = input[3];
input[3] = input[2];
input[2] = input[1];
input[1] = input[0];
input[0] = temp;
}
return input;
}

/*
* Inverse of the subBytes() method. Replaces values in the ciphertext
* with their corresponding inverses.
*/
private String[][] invSubBytes(String[][] ciphertext)
{

String temp, row, col = "";

for(int c = 0; c < 4; c++)
{
for(int d = 0; d < 4; d++)
{
temp = ciphertext[c][d];
if(temp.length() == 2)
{
col = String.valueOf(temp.charAt(1));
row = String.valueOf(temp.charAt(0));

ciphertext[c][d] = Integer.toHexString(invSbox[Integer.parseInt(col,n)][Integer.parseInt(row, n)]);

}
else
{
col = String.valueOf(temp.charAt(0));
ciphertext[c][d] = Integer.toHexString(invSbox[Integer.parseInt(col,n)][0]);
}
}
}
return ciphertext;

}

private String[][] invMixColumns(String[][] ciphertext)
{
String temp;
int matrixVal;

for(int x = 0; x < 4; x++)
{
for(int y = 0; y < 4; y++)
{
temp = ciphertext[x][y];
matrixVal = invColumnMatrix[x][y];

if(matrixVal == 9)
{
ciphertext[x][y] = Integer.toHexString(galoisMultiply(temp,9));
}
else if(matrixVal == 11)
{
ciphertext[x][y] = Integer.toHexString(galoisMultiply(temp,11));
}
else if(matrixVal == 13)
{
ciphertext[x][y] = Integer.toHexString(galoisMultiply(temp,13));
}
else if(matrixVal == 14)
{
ciphertext[x][y] = Integer.toHexString(galoisMultiply(temp,14));
}
}
}
return ciphertext;
}
}```