AES Algorithm

The Advanced Encryption Standard (AES) algorithm is a symmetric key encryption technique that has become the industry standard for securing data across various platforms. Developed by Vincent Rijmen and Joan Daemen in the late 1990s as a replacement for the Data Encryption Standard (DES), AES was adopted by the US government in 2002 after a rigorous evaluation process. The algorithm is based on a block cipher that encrypts and decrypts data in fixed-size blocks, typically 128 bits, with the key size determining the number of encryption rounds. AES supports key sizes of 128, 192, and 256 bits, with 10, 12, or 14 rounds of encryption respectively, providing robust security and making it highly resistant to cryptanalysis and brute-force attacks. AES operates on a structure called the state, which is an array of bytes, and goes through a sequence of transformations to encrypt or decrypt the data. The encryption process consists of four primary operations: SubBytes, ShiftRows, MixColumns, and AddRoundKey. These operations are applied iteratively for the specified number of rounds based on the key size. The decryption process involves inverse operations of encryption in reverse order. AES has been extensively analyzed, and its security and efficiency have been proven in various applications such as securing communication over the internet, protecting sensitive information in payment systems, and safeguarding classified information in government organizations. Its widespread adoption and strong security make AES a crucial tool in modern cryptography.
package ciphers;

import java.math.BigInteger;
import java.util.Scanner;

/**
 * This class is build to demonstrate the application of the AES-algorithm on a
 * single 128-Bit block of data.
 *
 */
public class AES {

	/**
	 * Precalculated values for x to the power of 2 in Rijndaels galois field. Used
	 * as 'RCON' during the key expansion.
	 */
	private static final int[] RCON = { 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
			0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91,
			0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74,
			0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
			0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4,
			0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,
			0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc,
			0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61,
			0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
			0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97,
			0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25,
			0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
			0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4,
			0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33,
			0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d };

	/**
	 * Rijndael S-box Substitution table used for encryption in the subBytes step,
	 * as well as the key expansion.
	 */
	private static final 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 Rijndael S-box Substitution table used for decryption in the
	 * subBytesDec step.
	 */
	private static final int[] INVERSE_SBOX = { 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 };

	/**
	 * Precalculated lookup table for galois field multiplication by 2 used in the
	 * MixColums step during encryption.
	 */
	private static final int[] MULT2 = { 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 };

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

	/**
	 * Precalculated lookup table for galois field multiplication by 9 used in the
	 * MixColums step during decryption.
	 */
	private static final int[] MULT9 = { 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 };

	/**
	 * Precalculated lookup table for galois field multiplication by 11 used in the
	 * MixColums step during decryption.
	 */
	private static final int[] MULT11 = { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74,
			0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2,
			0xd9, 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 };

	/**
	 * Precalculated lookup table for galois field multiplication by 13 used in the
	 * MixColums step during decryption.
	 */
	private static final int[] MULT13 = { 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, 0xb7,
			0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc, 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 };

	/**
	 * Precalculated lookup table for galois field multiplication by 14 used in the
	 * MixColums step during decryption.
	 */
	private static final int[] MULT14 = { 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, 0xad, 0xa3, 0xb1,
			0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 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 };

	/**
	 * Subroutine of the Rijndael key expansion.
	 *
	 * @param t
	 * @param rconCounter
	 * @return
	 */
	public static BigInteger scheduleCore(BigInteger t, int rconCounter) {
		String rBytes = t.toString(16);

		// Add zero padding
		while (rBytes.length() < 8) {
			rBytes = "0" + rBytes;
		}

		// rotate the first 16 bits to the back
		String rotatingBytes = rBytes.substring(0, 2);
		String fixedBytes = rBytes.substring(2);

		rBytes = fixedBytes + rotatingBytes;

		// apply S-Box to all 8-Bit Substrings
		for (int i = 0; i < 4; i++) {
			String currentByteBits = rBytes.substring(i * 2, (i + 1) * 2);

			int currentByte = Integer.parseInt(currentByteBits, 16);
			currentByte = SBOX[currentByte];

			// add the current RCON value to the first byte
			if (i == 0) {
				currentByte = currentByte ^ RCON[rconCounter];
			}

			currentByteBits = Integer.toHexString(currentByte);

			// Add zero padding

			while (currentByteBits.length() < 2) {
				currentByteBits = '0' + currentByteBits;
			}

			// replace bytes in original string
			rBytes = rBytes.substring(0, i * 2) + currentByteBits + rBytes.substring((i + 1) * 2);
		}

		// t = new BigInteger(rBytes, 16);
		// return t;
		return new BigInteger(rBytes, 16);
	}

	/**
	 *
	 * Returns an array of 10 + 1 round keys that are calculated by using Rijndael
	 * key schedule
	 *
	 * @param initialKey
	 * @return array of 10 + 1 round keys
	 */
	public static BigInteger[] keyExpansion(BigInteger initialKey) {
		BigInteger[] roundKeys = { initialKey, new BigInteger("0"), new BigInteger("0"), new BigInteger("0"),
				new BigInteger("0"), new BigInteger("0"), new BigInteger("0"), new BigInteger("0"), new BigInteger("0"),
				new BigInteger("0"), new BigInteger("0"), };

		// initialize rcon iteration
		int rconCounter = 1;

		for (int i = 1; i < 11; i++) {

			// get the previous 32 bits the key
			BigInteger t = roundKeys[i - 1].remainder(new BigInteger("100000000", 16));

			// split previous key into 8-bit segments
			BigInteger[] prevKey = { roundKeys[i - 1].remainder(new BigInteger("100000000", 16)),
					roundKeys[i - 1].remainder(new BigInteger("10000000000000000", 16))
							.divide(new BigInteger("100000000", 16)),
					roundKeys[i - 1].remainder(new BigInteger("1000000000000000000000000", 16))
							.divide(new BigInteger("10000000000000000", 16)),
					roundKeys[i - 1].divide(new BigInteger("1000000000000000000000000", 16)), };

			// run schedule core
			t = scheduleCore(t, rconCounter);
			rconCounter += 1;

			// Calculate partial round key
			BigInteger t0 = t.xor(prevKey[3]);
			BigInteger t1 = t0.xor(prevKey[2]);
			BigInteger t2 = t1.xor(prevKey[1]);
			BigInteger t3 = t2.xor(prevKey[0]);

			// Join round key segments
			t2 = t2.multiply(new BigInteger("100000000", 16));
			t1 = t1.multiply(new BigInteger("10000000000000000", 16));
			t0 = t0.multiply(new BigInteger("1000000000000000000000000", 16));
			roundKeys[i] = t0.add(t1).add(t2).add(t3);

		}
		return roundKeys;
	}

	/**
	 * representation of the input 128-bit block as an array of 8-bit integers.
	 *
	 * @param block
	 *            of 128-bit integers
	 * @return array of 8-bit integers
	 */
	public static int[] splitBlockIntoCells(BigInteger block) {

		int[] cells = new int[16];
		String blockBits = block.toString(2);

		// Append leading 0 for full "128-bit" string
		while (blockBits.length() < 128) {
			blockBits = '0' + blockBits;
		}

		// split 128 to 8 bit cells
		for (int i = 0; i < cells.length; i++) {
			String cellBits = blockBits.substring(8 * i, 8 * (i + 1));
			cells[i] = Integer.parseInt(cellBits, 2);
		}

		return cells;
	}

	/**
	 * Returns the 128-bit BigInteger representation of the input of an array of
	 * 8-bit integers.
	 *
	 * @param cells
	 *            that we need to merge
	 * @return block of merged cells
	 */
	public static BigInteger mergeCellsIntoBlock(int[] cells) {

		String blockBits = "";
		for (int i = 0; i < 16; i++) {
			String cellBits = Integer.toBinaryString(cells[i]);

			// Append leading 0 for full "8-bit" strings
			while (cellBits.length() < 8) {
				cellBits = '0' + cellBits;
			}

			blockBits += cellBits;
		}

		return new BigInteger(blockBits, 2);
	}

	/**
	 *
	 * @param ciphertext
	 * @param key
	 * @return ciphertext XOR key
	 */
	public static BigInteger addRoundKey(BigInteger ciphertext, BigInteger key) {
		return ciphertext.xor(key);
	}

	/**
	 * substitutes 8-Bit long substrings of the input using the S-Box and returns
	 * the result.
	 *
	 * @param ciphertext
	 * @return subtraction Output
	 */
	public static BigInteger subBytes(BigInteger ciphertext) {

		int[] cells = splitBlockIntoCells(ciphertext);

		for (int i = 0; i < 16; i++) {
			cells[i] = SBOX[cells[i]];
		}

		return mergeCellsIntoBlock(cells);
	}

	/**
	 * substitutes 8-Bit long substrings of the input using the inverse S-Box for
	 * decryption and returns the result.
	 *
	 * @param ciphertext
	 * @return subtraction Output
	 */
	public static BigInteger subBytesDec(BigInteger ciphertext) {

		int[] cells = splitBlockIntoCells(ciphertext);

		for (int i = 0; i < 16; i++) {
			cells[i] = INVERSE_SBOX[cells[i]];
		}

		return mergeCellsIntoBlock(cells);
	}

	/**
	 * Cell permutation step. Shifts cells within the rows of the input and returns
	 * the result.
	 *
	 * @param ciphertext
	 */
	public static BigInteger shiftRows(BigInteger ciphertext) {
		int[] cells = splitBlockIntoCells(ciphertext);
		int[] output = new int[16];

		// do nothing in the first row
		output[0] = cells[0];
		output[4] = cells[4];
		output[8] = cells[8];
		output[12] = cells[12];

		// shift the second row backwards by one cell
		output[1] = cells[5];
		output[5] = cells[9];
		output[9] = cells[13];
		output[13] = cells[1];

		// shift the third row backwards by two cell
		output[2] = cells[10];
		output[6] = cells[14];
		output[10] = cells[2];
		output[14] = cells[6];

		// shift the forth row backwards by tree cell
		output[3] = cells[15];
		output[7] = cells[3];
		output[11] = cells[7];
		output[15] = cells[11];

		return mergeCellsIntoBlock(output);
	}

	/**
	 * Cell permutation step for decryption . Shifts cells within the rows of the
	 * input and returns the result.
	 *
	 * @param ciphertext
	 */
	public static BigInteger shiftRowsDec(BigInteger ciphertext) {
		int[] cells = splitBlockIntoCells(ciphertext);
		int[] output = new int[16];

		// do nothing in the first row
		output[0] = cells[0];
		output[4] = cells[4];
		output[8] = cells[8];
		output[12] = cells[12];

		// shift the second row forwards by one cell
		output[1] = cells[13];
		output[5] = cells[1];
		output[9] = cells[5];
		output[13] = cells[9];

		// shift the third row forwards by two cell
		output[2] = cells[10];
		output[6] = cells[14];
		output[10] = cells[2];
		output[14] = cells[6];

		// shift the forth row forwards by tree cell
		output[3] = cells[7];
		output[7] = cells[11];
		output[11] = cells[15];
		output[15] = cells[3];

		return mergeCellsIntoBlock(output);
	}

	/**
	 * Applies the Rijndael MixColumns to the input and returns the result.
	 *
	 * @param ciphertext
	 */
	public static BigInteger mixColumns(BigInteger ciphertext) {

		int[] cells = splitBlockIntoCells(ciphertext);
		int[] outputCells = new int[16];

		for (int i = 0; i < 4; i++) {
			int[] row = { cells[i * 4], cells[i * 4 + 1], cells[i * 4 + 2], cells[i * 4 + 3] };

			outputCells[i * 4] = MULT2[row[0]] ^ MULT3[row[1]] ^ row[2] ^ row[3];
			outputCells[i * 4 + 1] = row[0] ^ MULT2[row[1]] ^ MULT3[row[2]] ^ row[3];
			outputCells[i * 4 + 2] = row[0] ^ row[1] ^ MULT2[row[2]] ^ MULT3[row[3]];
			outputCells[i * 4 + 3] = MULT3[row[0]] ^ row[1] ^ row[2] ^ MULT2[row[3]];
		}
		return mergeCellsIntoBlock(outputCells);
	}

	/**
	 * Applies the inverse Rijndael MixColumns for decryption to the input and
	 * returns the result.
	 *
	 * @param ciphertext
	 */
	public static BigInteger mixColumnsDec(BigInteger ciphertext) {

		int[] cells = splitBlockIntoCells(ciphertext);
		int[] outputCells = new int[16];

		for (int i = 0; i < 4; i++) {
			int[] row = { cells[i * 4], cells[i * 4 + 1], cells[i * 4 + 2], cells[i * 4 + 3] };

			outputCells[i * 4] = MULT14[row[0]] ^ MULT11[row[1]] ^ MULT13[row[2]] ^ MULT9[row[3]];
			outputCells[i * 4 + 1] = MULT9[row[0]] ^ MULT14[row[1]] ^ MULT11[row[2]] ^ MULT13[row[3]];
			outputCells[i * 4 + 2] = MULT13[row[0]] ^ MULT9[row[1]] ^ MULT14[row[2]] ^ MULT11[row[3]];
			outputCells[i * 4 + 3] = MULT11[row[0]] ^ MULT13[row[1]] ^ MULT9[row[2]] ^ MULT14[row[3]];
		}
		return mergeCellsIntoBlock(outputCells);
	}

	/**
	 * Encrypts the plaintext with the key and returns the result
	 *
	 * @param plainText
	 *            which we want to encrypt
	 * @param key
	 *            the key for encrypt
	 * @return EncryptedText
	 */
	public static BigInteger encrypt(BigInteger plainText, BigInteger key) {
		BigInteger[] roundKeys = keyExpansion(key);

		// Initial round
		plainText = addRoundKey(plainText, roundKeys[0]);

		// Main rounds
		for (int i = 1; i < 10; i++) {
			plainText = subBytes(plainText);
			plainText = shiftRows(plainText);
			plainText = mixColumns(plainText);
			plainText = addRoundKey(plainText, roundKeys[i]);
		}

		// Final round
		plainText = subBytes(plainText);
		plainText = shiftRows(plainText);
		plainText = addRoundKey(plainText, roundKeys[10]);

		return plainText;
	}

	/**
	 * Decrypts the ciphertext with the key and returns the result
	 *
	 * @param cipherText
	 *            The Encrypted text which we want to decrypt
	 * @param key
	 * @return decryptedText
	 */
	public static BigInteger decrypt(BigInteger cipherText, BigInteger key) {

		BigInteger[] roundKeys = keyExpansion(key);

		// Invert final round
		cipherText = addRoundKey(cipherText, roundKeys[10]);
		cipherText = shiftRowsDec(cipherText);
		cipherText = subBytesDec(cipherText);

		// Invert main rounds
		for (int i = 9; i > 0; i--) {
			cipherText = addRoundKey(cipherText, roundKeys[i]);
			cipherText = mixColumnsDec(cipherText);
			cipherText = shiftRowsDec(cipherText);
			cipherText = subBytesDec(cipherText);
		}

		// Invert initial round
		cipherText = addRoundKey(cipherText, roundKeys[0]);

		return cipherText;
	}

	public static void main(String[] args) {

		try (Scanner input = new Scanner(System.in)) {
			System.out.println("Enter (e) letter for encrpyt or (d) letter for decrypt :");
			char choice = input.nextLine().charAt(0);
			String in;
			switch (choice) {
			case 'E':
			case 'e':
				System.out.println("Choose a plaintext block (128-Bit Integer in base 16):");
				in = input.nextLine();
				BigInteger plaintext = new BigInteger(in, 16);
				System.out.println("Choose a Key (128-Bit Integer in base 16):");
				in = input.nextLine();
				BigInteger encryptionKey = new BigInteger(in, 16);
				System.out.println("The encrypted message is: \n" + encrypt(plaintext, encryptionKey).toString(16));
				break;
			case 'D':
			case 'd':
				System.out.println("Enter your ciphertext block (128-Bit Integer in base 16):");
				in = input.nextLine();
				BigInteger ciphertext = new BigInteger(in, 16);
				System.out.println("Choose a Key (128-Bit Integer in base 16):");
				in = input.nextLine();
				BigInteger decryptionKey = new BigInteger(in, 16);
				System.out.println("The deciphered message is:\n" + decrypt(ciphertext, decryptionKey).toString(16));
				break;
			default:
				System.out.println("** End **");
			}
		}

	}
}

LANGUAGE:

DARK MODE: