AES128实习

import java.security.InvalidKeyException;
import java.util.*;
import java.io.*;

// Brandon Senter & Thomas Le
// CS 457: Information Security
// Xunhua Wang; 09.27.2015

public class AES_NEW {

	public static String correctKey = "";

	public static void main(String[] args) throws Exception {
		long startTime = System.currentTimeMillis();

//		Scanner in = new Scanner(System.in);

		String fileName = "Le_Senter.txt";
		File out = new File(fileName);

		if (out.exists() == false) {
			out.createNewFile();
		}

//		System.out.println("Where to start?");
//		int start = in.nextInt();
//
//		System.out.println("Where to end?");
//		int end = in.nextInt();
//
//		in.close();
//
//		System.out.println("Starting AES_Decrpytion.java");

		decrypt(Integer.parseInt(args[0]), Integer.parseInt(args[1]));

		long endTime = System.currentTimeMillis();
		long totalTime = endTime - startTime;

		PrintWriter writer = new PrintWriter(fileName, "UTF-8");
		writer.println("Correct Key: " + correctKey);
		writer.println("Total runtime (ms): " + totalTime);
		writer.close();

//		System.out.println("Done.");
	}

	public static void decrypt(int start, int end) throws InvalidKeyException {
		byte[] potentialKey = new byte[16];
		potentialKey[15] = 0x03;
		for (int i = 5; i <= 14; i++)
			potentialKey[i] = 0x00;

		for (int i = start; i <= end; i++) {
			potentialKey[0] = (byte) i;
			for (int j = 0; j <= 255; j++) {
				potentialKey[1] = (byte) j;
				for (int k = 0; k <= 255; k++) {
					potentialKey[2] = (byte) k;
					for (int l = 0; l <= 255; l++) {
						potentialKey[3] = (byte) l;
						for (int m = 6; m <= 254; m += 8) {
							potentialKey[4] = (byte) m;
							
//							for (byte ki : potentialKey) System.out.print (String.format("%02X", ki & 0xff));
//							System.out.println ("");
							
							boolean isCorrect = testKey(potentialKey);
							if (isCorrect) {
								correctKey = potentialKey.toString();
								return;
							}
						} // m-loop
					} // l-loop
				} // k-loop
			} // j-loop
		} // i-loop
	} // decrypt()

	public static boolean testKey(byte[] inKey) throws InvalidKeyException {

		String IV_string = "5BF16CF65F7D2E547AAF6522342C30D8";
		String C1_string = "E84811608B52001CDD9FC48334433B6F";
		String C2_string = "C87C0B94F6A7747383881E18A6518DDF";
		String C3_string = "3E64B928FA5EFC67275D77D18F505335";
		String C4_string = "202F8CD7D8984917681CB1686D3239FB";

		byte[] iv = hexStringToByteArray(IV_string);
		byte[] c1 = hexStringToByteArray(C1_string);
		byte[] c2 = hexStringToByteArray(C2_string);
		byte[] c3 = hexStringToByteArray(C3_string);
		byte[] c4 = hexStringToByteArray(C4_string);

		Object roundKeys = null;

		roundKeys = Rijndael_Algorithm.makeKey(Rijndael_Algorithm.DECRYPT_MODE, inKey);

		byte[] returnArray = Rijndael_Algorithm.blockDecrypt2(c1, 0, roundKeys);

		if (keyCheck(iv, returnArray)) {
			byte[] returnArray2 = Rijndael_Algorithm.blockDecrypt2(c2, 0, roundKeys);

			if (keyCheck(returnArray, returnArray2)) {
				byte[] returnArray3 = Rijndael_Algorithm.blockDecrypt2(c3, 0, roundKeys);

				if (keyCheck(returnArray2, returnArray3)) {
					byte[] returnArray4 = Rijndael_Algorithm.blockDecrypt2(c4, 0, roundKeys);

					if (keyCheck(returnArray3, returnArray4))
						return true;
				}
			}
		}
		return false;
	}

	public static boolean keyCheck(byte[] previous, byte[] current) {
		if (checkASCII(previous, xor(previous, current)))
			return true;
		return false;
	}

	public static byte[] xor(byte[] previous, byte[] current) {

		for (int x = 0; x < current.length; x++) {
			current[x] = (byte) (current[x] ^ previous[x]);
		}
		return current;
	}

	public static boolean checkASCII(byte[] previous, byte[] current) {

		for (int y = 0; y < current.length; y++) {
			if ((current[y] < 32) || (current[y] >= 127)) {
				continue;
			} else {
				return false;
			}
		}
		return true;
	}

	public static byte[] hexStringToByteArray(String s) {
		int len = s.length();
		byte[] data = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
		}

		return data;
	}

}

// package sci.crypt.course.crypto;// import Rijndael_Algorithm;import java.security.SecureRandom;import java.math.BigInteger;import java.io.*;import java.util.*;/** * @author Xunhua Wang (wangxx@jmu.edu) * @date 09/27/2014 * All rights reserved */public class AESExample { public void testAESImplementationTwo () { try { int times = 1000; byte[] inKey = new byte[16]; byte[] cbcIV = new byte[16]; SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.nextBytes(inKey); // This generates a random AES-128 key random.nextBytes(cbcIV); // This generates a random IV // populate the plaintext String textString = "abcdefghijklmnop"; byte[] inText = textString.getBytes(); for (int i=0; i < 16; i++) inText[i] = (byte) (inText[i] ^ cbcIV[i]); // According to CBC, this is how we use the IV long time1 = 0, time2 = 0, time3 = 0, time4 = 0; Object roundKeys = null; byte[] cipherText = null; for (int i=0; i < times; i++) // To get more accurate timing results, we have to warm up your CPU a little bit.// This is the whole purpose of this line of code// This warm-up is NOT needed in your programming or real-world applications roundKeys = Rijndael_Algorithm.makeKey (Rijndael_Algorithm.ENCRYPT_MODE, inKey); // Now, we are ready and let's start the business System.out.println (System.getProperty ("line.separator") + "Testing AES implementation two ......"); time1 = System.currentTimeMillis(); // Start the timing clock // // Why do we do this 1000 times? If you choose to measure the time to perform a // single key expansion and encryption, you will always get 0, as this process is too fast // for (int i=0; i < times; i++) { // for measuring time. If you use 1 instead, you will always get 0 in time3 // AES key schedule roundKeys = Rijndael_Algorithm.makeKey (Rijndael_Algorithm.ENCRYPT_MODE, inKey); // AES encryption, the first parameter is 128-bit // plaintext, the second parameter is 0, and the // third parameter is the round keys generated by the // key schedule call cipherText = Rijndael_Algorithm.blockEncrypt2 (inText, 0, roundKeys); } time2 = System.currentTimeMillis(); // end the timing clock time3 = time2 - time1; // This is the time, in milliseconds, elapsed in this 1000 BLOCK encryptions // // Now it is time to decrypt the ciphertext // byte[] recoveredText = null; time1 = System.currentTimeMillis(); // Start the timing clock for (int i=0; i < times; i++) { roundKeys = Rijndael_Algorithm.makeKey (Rijndael_Algorithm.DECRYPT_MODE, inKey); recoveredText = Rijndael_Algorithm.blockDecrypt2 (cipherText, 0, roundKeys); } time2 = System.currentTimeMillis(); // End the timing clock time4 = time2 - time1; // This is the time, in milliseconds, elapsed in this 1000 BLOCK decryptions // How do we know whether the decryption works? We have to check the recovered cleartext for (int i=0; i < 16; i++) recoveredText[i] = (byte) (recoveredText[i] ^ cbcIV[i]); String recoveredString = new String (recoveredText); if (!recoveredString.equals (textString)) { System.out.println ("Decryption does NOT work!"); } else System.out.println ("Decryption worked beautifully and recovered the original plaintext!"); System.out.println ("It takes blockEncrypt2 " + time3 + " milliseconds to run AES-128 key scheduling & ENcryption " + times + " times"); System.out.println ("It takes blockDecrypt2 " + time4 + " milliseconds to run AES-128 key scheduling & DEcryption " + times + " times"); } catch (Exception ex) { ex.printStackTrace(); } } public void testAESSlowerImplementationOne () { try { int times = 1000; byte[] inKey = new byte[16]; byte[] cbcIV = new byte[16]; SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.nextBytes(inKey); // This generates a random AES-128 key random.nextBytes(cbcIV); // This generates a random IV // populate the plaintext String textString = "abcdefghijklmnop"; byte[] inText = textString.getBytes(); for (int i=0; i < 16; i++) inText[i] = (byte) (inText[i] ^ cbcIV[i]); // According to CBC, this is how we use the IV long time1 = 0, time2 = 0, time3 = 0, time4 = 0; Object roundKeys = null; byte[] cipherText = null; for (int i=0; i < times; i++) // To get more accurate timing results, we have to warm up your CPU a little bit.// This is the whole purpose of this line of code// This warm-up is NOT needed in your programming or real-world applications roundKeys = Rijndael_Algorithm.makeKey (Rijndael_Algorithm.ENCRYPT_MODE, inKey); // Now, we are ready and let's start the business System.out.println (System.getProperty ("line.separator") + "Testing AES implementation one, which is SLOWER ......"); time1 = System.currentTimeMillis(); // Start the timing clock // // Why do we do this 1000 times? If you choose to measure the time to perform a // single key expansion and encryption, you will always get 0, as this process is too fast // for (int i=0; i < times; i++) { // for measuring time. If you use 1 instead, you will always get 0 in time3 // AES key schedule roundKeys = Rijndael_Algorithm.makeKey (Rijndael_Algorithm.ENCRYPT_MODE, inKey); // AES encryption, the first parameter is 128-bit // plaintext, the second parameter is 0, and the // third parameter is the round keys generated by the // key schedule call cipherText = Rijndael_Algorithm.blockEncrypt (inText, 0, roundKeys); } time2 = System.currentTimeMillis(); // end the timing clock time3 = time2 - time1; // This is the time, in milliseconds, elapsed in this 1000 BLOCK encryptions // // Now it is time to decrypt the ciphertext // byte[] recoveredText = null; time1 = System.currentTimeMillis(); // Start the timing clock for (int i=0; i < times; i++) { roundKeys = Rijndael_Algorithm.makeKey (Rijndael_Algorithm.ENCRYPT_MODE, inKey); // Yes, you have to specify the ENCRYPTION_MODE for this slower implementation recoveredText = Rijndael_Algorithm.blockDecrypt (cipherText, 0, roundKeys); } time2 = System.currentTimeMillis(); // End the timing clock time4 = time2 - time1; // This is the time, in milliseconds, elapsed in this 1000 BLOCK decryptions // How do we know whether the decryption works? We have to check the recovered cleartext for (int i=0; i < 16; i++) recoveredText[i] = (byte) (recoveredText[i] ^ cbcIV[i]); String recoveredString = new String (recoveredText); System.out.println(recoveredString); if (!recoveredString.equals (textString)) { System.out.println ("Decryption does NOT work!"); } else System.out.println ("Decryption worked beautifully and recovered the original plaintext!"); System.out.println ("It takes blockEncrypt " + time3 + " milliseconds to run AES-128 key scheduling & ENcryption " + times + " times"); System.out.println ("It takes blockDecrypt " + time4 + " milliseconds to run AES-128 key scheduling & DEcryption " + times + " times"); } catch (Exception ex) { ex.printStackTrace(); } } public static String convertToString (byte[] data) {char[] _hexArray = {'0', '1', '2', '3', '4', '5','6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};StringBuffer sb = new StringBuffer();for (int i=0; i <data.length; i++) {sb.append("" + _hexArray[(data[i] >> 4) & 0x0f] + _hexArray[data[i] & 0x0f]);}return sb.toString(); } public static void main (String[] args) { System.out.println("Starting..."); System.out.println("..."); try { AESExample aes = new AESExample(); aes.testAESImplementationTwo();aes.testAESSlowerImplementationOne (); } catch (Exception ex) { ex.printStackTrace(); } }}


/**
 * @author Xunhua Wang (wangxx@jmu.edu). All rights reserved.
 * @date 09/25/2014
 */

public class AES_Structure {
	
	public void crack (int start, int end) {

		byte[] myguessedkey = new byte[16];
		for (int i = 0; i < 16; i ++) myguessedkey[i] = (byte) 0x00; // This may be unnecessary but to make sure ...
		myguessedkey[15] = (byte) 0x03;

		for (int i = start; i <= end; i++) {
			for (int j = 0; j <= 255; j++) {
				for (int k = 0; k <= 255; k++) {
					for (int x = 0; x <= 255; x++) {
						for (int y = 6; y <= 255; y+=8) { // Why does y start from 6 and increment by 8? Think!
							myguessedkey[0] = (byte) i;
	 						myguessedkey[1] = (byte) j;
	 						myguessedkey[2] = (byte) k; 	
							myguessedkey[3] = (byte) x;
	 						myguessedkey[4] = (byte) y;

							//
							// In my test run, print out myguessedkey to make sure that it is correct
							// This line of code prints out and slows things down	
							//
							//	TODO: the following two lines should be commented out when you start running your code
							//
							for (byte ki : myguessedkey) System.out.print (String.format("%02X", ki & 0xff));
							System.out.println ("");

							//
							// Next, we will test whether this guessed key is good or not
							// TODO: YOU need to fill in the details here
							//
							boolean isCorrectKey = testOneKey (myguessedkey);

							if (isCorrectKey) return;
						}
					}
				}
			}
		}
	}

	//
	// Method UNFINISHED
	//
	public boolean testOneKey (byte[] inKey) {
		//
		// Is this key correct? How to find out?
		//

		//
		// Step 0. Prepare the IV and ciphertext blocks
		//
		String ivStr = "FEE6CB5BE4BBFC3DC623B8FE9F0006B2";
		String c1Str = "C2B8B6A64DB0E2101B147381442C271C";
		String c2Str = "33845FD95F0C589680CB822C6A0CB950";
		String c3Str = "F4190D6C5D15FC968756DF5E229F32FA";
		String c4Str = "C8406CF6E3C812EED825CB15970AFBB7";

		byte[] iv = hexStringToByteArray (ivStr);
		byte[] c1 = hexStringToByteArray (c1Str);
		byte[] c2 = hexStringToByteArray (c2Str);
		byte[] c3 = hexStringToByteArray (c3Str);
		byte[] c4 = hexStringToByteArray (c4Str);

		//
		// Step 1. Prepare for the AES round keys
		//
		Object aesRoundKeys = null;

		try {
			aesRoundKeys = Rijndael_Algorithm.makeKey(Rijndael_Algorithm.DECRYPT_MODE, inKey);
		} catch (Exception ex) {
			ex.printStackTrace ();

			return false;
		}

		//
		// Step 2. Now decrypt ciphertext block 1 _and_ XOR with IV; UNFINISHED
		//	
		byte[] returnArray = Rijndael_Algorithm.blockDecrypt2(c1, 0, aesRoundKeys);
		// TODO: XOR with IV and test whether the cleartext block is good or not. If not, return false
		//	Otherwise, continue
		//

		//
		// Step 3. Now decrypt ciphertext block 2 _and_ XOR with c1; UNFINISHED
		//	
		byte[] returnArray2 = Rijndael_Algorithm.blockDecrypt2(c2, 0, aesRoundKeys);
		// TODO: XOR with c1 and test whether the cleartext block is good or not. If not, return false
		//	Otherwise, continue
		//

		//
		// Step 4. Now decrypt ciphertext block 3 _and_ XOR with c2; UNFINISHED
		//	
		byte[] returnArray3 = Rijndael_Algorithm.blockDecrypt2(c3, 0, aesRoundKeys);
		// TODO: XOR with c2 and test whether the cleartext block is good or not. If not, return false
		//	Otherwise, continue
		//

		//
		// Step 5. Now decrypt ciphertext block 4 _and_ XOR with c3; UNFINISHED
		//	
		byte[] returnArray4 = Rijndael_Algorithm.blockDecrypt2(c4, 0, aesRoundKeys);
		// TODO: XOR with c3 and test whether the cleartext block is good or not. If not, return false
		//	Otherwise, found the key; print the key and all four plaintext blocks to a file
		//
		
		//
		// The following line should be removed after you complete the code
		//
		return false;
		
	}

	// 
	// The following method is copied from Medovar and Sharp. Thank them for the code when you get a chance
	// 
	public static byte[] hexStringToByteArray(String s) {
		int len = s.length();
		byte[] data = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
		}

		return data;
	}

	public static void main (String args[]) {
		if (args.length < 2) {
			System.out.println ("Use java AESChallengeCrackerWithFiveLoops start end");
			return;
		}

		try {
			int start = Integer.parseInt (args[0]);
			int end = Integer.parseInt (args[1]);

			AES_Structure acc = new AES_Structure ();

			acc.crack (start, end);
		} catch (Exception ex) {
			ex.printStackTrace ();
		}
	}
}

import java.security.InvalidKeyException;
import java.util.*;
import java.io.*;

// Brandon Senter & Thomas Le
// CS 457: Information Security
// Xunhua Wang; 09.27.2015

public class AES_NEW {

	public static String correctKey = "";

	public static void main(String[] args) throws Exception {
		long startTime = System.currentTimeMillis();

//		Scanner in = new Scanner(System.in);

		String fileName = "Le_Senter.txt";
		File out = new File(fileName);

		if (out.exists() == false) {
			out.createNewFile();
		}

//		System.out.println("Where to start?");
//		int start = in.nextInt();
//
//		System.out.println("Where to end?");
//		int end = in.nextInt();
//
//		in.close();
//
//		System.out.println("Starting AES_Decrpytion.java");

		decrypt(Integer.parseInt(args[0]), Integer.parseInt(args[1]));

		long endTime = System.currentTimeMillis();
		long totalTime = endTime - startTime;

		PrintWriter writer = new PrintWriter(fileName, "UTF-8");
		writer.println("Correct Key: " + correctKey);
		writer.println("Total runtime (ms): " + totalTime);
		writer.close();

//		System.out.println("Done.");
	}

	public static void decrypt(int start, int end) throws InvalidKeyException {
		byte[] potentialKey = new byte[16];
		potentialKey[15] = 0x03;
		for (int i = 5; i <= 14; i++)
			potentialKey[i] = 0x00;

		for (int i = start; i <= end; i++) {
			potentialKey[0] = (byte) i;
			for (int j = 0; j <= 255; j++) {
				potentialKey[1] = (byte) j;
				for (int k = 0; k <= 255; k++) {
					potentialKey[2] = (byte) k;
					for (int l = 0; l <= 255; l++) {
						potentialKey[3] = (byte) l;
						for (int m = 6; m <= 254; m += 8) {
							potentialKey[4] = (byte) m;
							
//							for (byte ki : potentialKey) System.out.print (String.format("%02X", ki & 0xff));
//							System.out.println ("");
							
							boolean isCorrect = testKey(potentialKey);
							if (isCorrect) {
								correctKey = potentialKey.toString();
								return;
							}
						} // m-loop
					} // l-loop
				} // k-loop
			} // j-loop
		} // i-loop
	} // decrypt()

	public static boolean testKey(byte[] inKey) throws InvalidKeyException {

		String IV_string = "5BF16CF65F7D2E547AAF6522342C30D8";
		String C1_string = "E84811608B52001CDD9FC48334433B6F";
		String C2_string = "C87C0B94F6A7747383881E18A6518DDF";
		String C3_string = "3E64B928FA5EFC67275D77D18F505335";
		String C4_string = "202F8CD7D8984917681CB1686D3239FB";

		byte[] iv = hexStringToByteArray(IV_string);
		byte[] c1 = hexStringToByteArray(C1_string);
		byte[] c2 = hexStringToByteArray(C2_string);
		byte[] c3 = hexStringToByteArray(C3_string);
		byte[] c4 = hexStringToByteArray(C4_string);

		Object roundKeys = null;

		roundKeys = Rijndael_Algorithm.makeKey(Rijndael_Algorithm.DECRYPT_MODE, inKey);

		byte[] returnArray = Rijndael_Algorithm.blockDecrypt2(c1, 0, roundKeys);

		if (keyCheck(iv, returnArray)) {
			byte[] returnArray2 = Rijndael_Algorithm.blockDecrypt2(c2, 0, roundKeys);

			if (keyCheck(returnArray, returnArray2)) {
				byte[] returnArray3 = Rijndael_Algorithm.blockDecrypt2(c3, 0, roundKeys);

				if (keyCheck(returnArray2, returnArray3)) {
					byte[] returnArray4 = Rijndael_Algorithm.blockDecrypt2(c4, 0, roundKeys);

					if (keyCheck(returnArray3, returnArray4))
						return true;
				}
			}
		}
		return false;
	}

	public static boolean keyCheck(byte[] previous, byte[] current) {
		if (checkASCII(previous, xor(previous, current)))
			return true;
		return false;
	}

	public static byte[] xor(byte[] previous, byte[] current) {

		for (int x = 0; x < current.length; x++) {
			current[x] = (byte) (current[x] ^ previous[x]);
		}
		return current;
	}

	public static boolean checkASCII(byte[] previous, byte[] current) {

		for (int y = 0; y < current.length; y++) {
			if ((current[y] < 32) || (current[y] >= 127)) {
				continue;
			} else {
				return false;
			}
		}
		return true;
	}

	public static byte[] hexStringToByteArray(String s) {
		int len = s.length();
		byte[] data = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
		}

		return data;
	}

}

// $Id: Rijndael_Algorithm.java,v 1.1.1.1 2005/10/11 16:49:07 ruthap Exp $
//
// $Log: Rijndael_Algorithm.java,v $
// Revision 1.1.1.1  2005/10/11 16:49:07  ruthap
// My Research Source Code
//
// Revision 1.1  1998/04/12  Paulo
// + optimized methods for the default 128-bit block size.
//
// Revision 1.0  1998/03/11  Raif
// + original version.
//
// $Endlog$
/*
 * Copyright (c) 1997, 1998 Systemics Ltd on behalf of
 * the Cryptix Development Team. All rights reserved.
 */
// package Rijndael;

import java.io.PrintWriter;
import java.security.InvalidKeyException;

//...........................................................................
/**
 * Rijndael --pronounced Reindaal-- is a variable block-size (128-, 192- and
 * 256-bit), variable key-size (128-, 192- and 256-bit) symmetric cipher.<p>
 *
 * Rijndael was written by <a href="mailto:rijmen@esat.kuleuven.ac.be">Vincent
 * Rijmen</a> and <a href="mailto:Joan.Daemen@village.uunet.be">Joan Daemen</a>.<p>
 *
 * Portions of this code are <b>Copyright</b> &copy; 1997, 1998
 * <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
 * <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
 * <br>All rights reserved.<p>
 *
 * <b>$Revision: 1.1.1.1 $</b>
 * @author  Raif S. Naffah
 * @author  Paulo S. L. M. Barreto
 */
public final class Rijndael_Algorithm // implicit no-argument constructor
{
// Debugging methods and variables
//...........................................................................

    public static int ENCRYPT_MODE = 1;
    public static int DECRYPT_MODE = 2;
    public static int BOTH_MODE = 3;
    
    static final String NAME = "Rijndael_Algorithm";
    static final boolean IN = true, OUT = false;

    static final boolean DEBUG = Rijndael_Properties.GLOBAL_DEBUG;
    static final int debuglevel = DEBUG ? Rijndael_Properties.getLevel(NAME) : 0;
    static final PrintWriter err = DEBUG ? Rijndael_Properties.getOutput() : null;

    static final boolean TRACE = Rijndael_Properties.isTraceable(NAME);

    static void debug (String s) { err.println(">>> "+NAME+": "+s); }
    static void trace (boolean in, String s) {
        if (TRACE) err.println((in?"==> ":"<== ")+NAME+"."+s);
    }
    static void trace (String s) { if (TRACE) err.println("<=> "+NAME+"."+s); }


// Constants and variables
//...........................................................................

    static final int BLOCK_SIZE = 16; // default block size in bytes

    static final int[] alog = new int[256];
    static final int[] log =  new int[256];

    static final byte[] S =  new byte[256];
    static final byte[] Si = new byte[256];
    static final int[] T1 = new int[256];
    static final int[] T2 = new int[256];
    static final int[] T3 = new int[256];
    static final int[] T4 = new int[256];
    static final int[] T5 = new int[256];
    static final int[] T6 = new int[256];
    static final int[] T7 = new int[256];
    static final int[] T8 = new int[256];
    static final int[] U1 = new int[256];
    static final int[] U2 = new int[256];
    static final int[] U3 = new int[256];
    static final int[] U4 = new int[256];
    static final byte[] rcon = new byte[30];

    static final int[][][] shifts = new int[][][] {
        { {0, 0}, {1, 3}, {2, 2}, {3, 1} },
        { {0, 0}, {1, 5}, {2, 4}, {3, 3} },
        { {0, 0}, {1, 7}, {3, 5}, {4, 4} }
    };

    private static final char[] HEX_DIGITS = {
        '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
    };

    static byte[][] mixColumnsG = new byte[][] {
            {2, 3, 1, 1},
            {1, 2, 3, 1},
            {1, 1, 2, 3},
            {3, 1, 1, 2}
        };

    static byte[][] invMixColumnsG = new byte[][] {
            {(byte)0x0E, (byte)0x0B, (byte)0x0D, (byte)0x09},
            {(byte)0x09, (byte)0x0E, (byte)0x0B, (byte)0x0D},
            {(byte)0x0D, (byte)0x09, (byte)0x0E, (byte)0x0B},
            {(byte)0x0B, (byte)0x0D, (byte)0x09, (byte)0x0E}
        };

// Static code - to intialise S-boxes and T-boxes
//...........................................................................

    static {
        long time = System.currentTimeMillis();

	if (DEBUG && debuglevel > 6) {
		System.out.println("Algorithm Name: "+Rijndael_Properties.FULL_NAME);
		System.out.println("Electronic Codebook (ECB) Mode");
		System.out.println();
	}
        int ROOT = 0x11B;
        int i, j = 0;

        //
        // produce log and alog tables, needed for multiplying in the
        // field GF(2^m) (generator = 3)
        //
        alog[0] = 1;
        for (i = 1; i < 256; i++) {
            j = (alog[i-1] << 1) ^ alog[i-1];
            if ((j & 0x100) != 0) j ^= ROOT;
            alog[i] = j;
        }
        for (i = 1; i < 255; i++) log[alog[i]] = i;
        byte[][] A = new byte[][] {
            {1, 1, 1, 1, 1, 0, 0, 0},
            {0, 1, 1, 1, 1, 1, 0, 0},
		    {0, 0, 1, 1, 1, 1, 1, 0},
		    {0, 0, 0, 1, 1, 1, 1, 1},
		    {1, 0, 0, 0, 1, 1, 1, 1},
		    {1, 1, 0, 0, 0, 1, 1, 1},
		    {1, 1, 1, 0, 0, 0, 1, 1},
		    {1, 1, 1, 1, 0, 0, 0, 1}
		};
        byte[] B = new byte[] { 0, 1, 1, 0, 0, 0, 1, 1};

        //
        // substitution box based on F^{-1}(x)
        //
        int t;
        byte[][] box = new byte[256][8];
        box[1][7] = 1;
        for (i = 2; i < 256; i++) {
            j = alog[255 - log[i]];
            for (t = 0; t < 8; t++)
                box[i][t] = (byte)((j >>> (7 - t)) & 0x01);
        }
        //
        // affine transform:  box[i] <- B + A*box[i]
        //
        byte[][] cox = new byte[256][8];
        for (i = 0; i < 256; i++)
            for (t = 0; t < 8; t++) {
                cox[i][t] = B[t];
                for (j = 0; j < 8; j++)
                    cox[i][t] ^= A[t][j] * box[i][j];
            }
        //
        // S-boxes and inverse S-boxes
        //
        for (i = 0; i < 256; i++) {
            S[i] = (byte)(cox[i][0] << 7);
	        for (t = 1; t < 8; t++)
	            S[i] ^= cox[i][t] << (7-t);
            Si[S[i] & 0xFF] = (byte) i;
        }
        //
        // T-boxes
        //
        byte[][] G = new byte[][] {
            {2, 1, 1, 3},
            {3, 2, 1, 1},
            {1, 3, 2, 1},
            {1, 1, 3, 2}
        };
        byte[][] AA = new byte[4][8];
        for (i = 0; i < 4; i++) {
            for (j = 0; j < 4; j++) AA[i][j] = G[i][j];
            AA[i][i+4] = 1;
        }
        byte pivot, tmp;
        byte[][] iG = new byte[4][4];
        for (i = 0; i < 4; i++) {
            pivot = AA[i][i];
            if (pivot == 0) {
                t = i + 1;
                while ((AA[t][i] == 0) && (t < 4))
                    t++;
                if (t == 4)
                    throw new RuntimeException("G matrix is not invertible");
                else {
                    for (j = 0; j < 8; j++) {
                        tmp = AA[i][j];
                        AA[i][j] = AA[t][j];
                        AA[t][j] = (byte) tmp;
                    }
                    pivot = AA[i][i];
                }
            }
            for (j = 0; j < 8; j++)
                if (AA[i][j] != 0)
                    AA[i][j] = (byte)
                        alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255];
            for (t = 0; t < 4; t++)
                if (i != t) {
                    for (j = i+1; j < 8; j++)
                        AA[t][j] ^= mul(AA[i][j], AA[t][i]);
                    AA[t][i] = 0;
                }
        }
        for (i = 0; i < 4; i++)
            for (j = 0; j < 4; j++) iG[i][j] = AA[i][j + 4];

        int s;
        for (t = 0; t < 256; t++) {
            s = S[t];
            T1[t] = mul4(s, G[0]);
            T2[t] = mul4(s, G[1]);
            T3[t] = mul4(s, G[2]);
            T4[t] = mul4(s, G[3]);

            s = Si[t];
            T5[t] = mul4(s, iG[0]);
            T6[t] = mul4(s, iG[1]);
            T7[t] = mul4(s, iG[2]);
            T8[t] = mul4(s, iG[3]);

            U1[t] = mul4(t, iG[0]);
            U2[t] = mul4(t, iG[1]);
            U3[t] = mul4(t, iG[2]);
            U4[t] = mul4(t, iG[3]);
        }
        //
        // round constants
        //
        rcon[0] = 1;
        int r = 1;
        for (t = 1; t < 30; ) rcon[t++] = (byte)(r = mul(2, r));

        time = System.currentTimeMillis() - time;

	if (DEBUG && debuglevel > 8) {
		System.out.println("==========");
		System.out.println();
		System.out.println("Static Data");
		System.out.println();
		System.out.println("S[]:"); for(i=0;i<16;i++) { for(j=0;j<16;j++) System.out.print("0x"+byteToString(S[i*16+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("Si[]:"); for(i=0;i<16;i++) { for(j=0;j<16;j++) System.out.print("0x"+byteToString(Si[i*16+j])+", "); System.out.println();}

		System.out.println();
		System.out.println("iG[]:"); for(i=0;i<4;i++){for(j=0;j<4;j++) System.out.print("0x"+byteToString(iG[i][j])+", "); System.out.println();}

		System.out.println();
		System.out.println("T1[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T1[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T2[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T2[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T3[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T3[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T4[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T4[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T5[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T5[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T6[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T6[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T7[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T7[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("T8[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T8[i*4+j])+", "); System.out.println();}

		System.out.println();
		System.out.println("U1[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U1[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("U2[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U2[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("U3[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U3[i*4+j])+", "); System.out.println();}
		System.out.println();
		System.out.println("U4[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U4[i*4+j])+", "); System.out.println();}

		System.out.println();
		System.out.println("rcon[]:"); for(i=0;i<5;i++){for(j=0;j<6;j++) System.out.print("0x"+byteToString(rcon[i*6+j])+", "); System.out.println();}

		System.out.println();
		System.out.println("Total initialization time: "+time+" ms.");
		System.out.println();
	}
    }

    // multiply two elements of GF(2^m)
    static final int mul (int a, int b) {
        return (a != 0 && b != 0) ?
            alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] :
            0;
    }

    // convenience method used in generating Transposition boxes
    static final int mul4 (int a, byte[] b) {
        if (a == 0) return 0;
        a = log[a & 0xFF];
        int a0 = (b[0] != 0) ? alog[(a + log[b[0] & 0xFF]) % 255] & 0xFF : 0;
        int a1 = (b[1] != 0) ? alog[(a + log[b[1] & 0xFF]) % 255] & 0xFF : 0;
        int a2 = (b[2] != 0) ? alog[(a + log[b[2] & 0xFF]) % 255] & 0xFF : 0;
        int a3 = (b[3] != 0) ? alog[(a + log[b[3] & 0xFF]) % 255] & 0xFF : 0;
        return a0 << 24 | a1 << 16 | a2 << 8 | a3;
    }

	static final byte mulOneRowOneCol (byte[] inA, byte[] inB) {
		byte result = (byte) 0x0;
		for (int i=0; i < 4; i++) {
			int tmpInt = mul (inA[i], inB[i]);
			result = (byte) ((result ^ tmpInt) & 0xFF);
		}

		return result;
	}


 THE FIRST IMPLEMENTATION \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

// Basic API methods
//...........................................................................

    /**
     * Convenience method to expand a user-supplied key material into a
     * session key, assuming Rijndael's default block size (128-bit).
     *
     * @param key The 128/192/256-bit user-key to use.
     * @exception  InvalidKeyException  If the key is invalid.
     */
    public static Object makeKey (int mode, byte[] k) throws InvalidKeyException {
        return makeKey(mode, k, BLOCK_SIZE);
    }

    /**
     * NOTE: Xunhua Wang added this method on 02/20/2014
     *
     * Convenience method to encrypt exactly one block of plaintext, assuming
     * Rijndael's default block size (128-bit).
     *
     * @param  in         The plaintext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for encryption.
     * @return The ciphertext generated from a plaintext using the session key.
     */
    public static byte[]
    blockEncrypt (byte[] in, int inOffset, Object sessionKey) 
    {
	if (DEBUG) trace(IN, "blockEncrypt("+in+", "+inOffset+", "+sessionKey+")");
        int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
        int ROUNDS = Ke.length - 1;

        int[] Ker = Ke[0];

	byte[] internalState = new byte[16]; // XHW, 02/20/2014
	byte[] roundKeyBytes = new byte[16];
	int counter = 0;
	for (int i = 0; i < 4; i++) {
		roundKeyBytes[counter++] = (byte) (Ker[i] >> 24 & 0xFF);
		roundKeyBytes[counter++] = (byte) (Ker[i] >> 16 & 0xFF);
		roundKeyBytes[counter++] = (byte) (Ker[i] >> 8 & 0xFF);
		roundKeyBytes[counter++] = (byte) (Ker[i] & 0xFF);
	}

	for (int i = 0; i < 16; i++)
		internalState[i] = (byte) ((in[inOffset + i] ^ roundKeyBytes[i]) & 0xFF);

	for (int r = 1; r <= ROUNDS; r++) {          // apply round transforms
		Ker = Ke[r];
		counter = 0;
		for (int i = 0; i < 4; i++) {
			roundKeyBytes[counter++] = (byte) (Ker[i] >> 24 & 0xFF);
			roundKeyBytes[counter++] = (byte) (Ker[i] >> 16 & 0xFF);
			roundKeyBytes[counter++] = (byte) (Ker[i] >> 8 & 0xFF);
			roundKeyBytes[counter++] = (byte) (Ker[i] & 0xFF);
		}

		for (int i = 0; i < 16; i++) 
			internalState[i] = S[internalState[i] & 0xFF];

		byte tmpByte = internalState[1];
		internalState[1] = internalState[5];
		internalState[5] = internalState[9];
		internalState[9] = internalState[13];
		internalState[13] = tmpByte;

		tmpByte = internalState[2];
		byte tmpByte2 = internalState[6];
		internalState[2] = internalState[10];
		internalState[6] = internalState[14];
		internalState[10] = tmpByte;
		internalState[14] = tmpByte2;

		tmpByte = internalState[15];
		internalState[15] = internalState[11];
		internalState[11] = internalState[7];
		internalState[7] = internalState[3];
		internalState[3] = tmpByte;

		if (r != ROUNDS) {
			for (int i = 0; i < 4; i++) { // column of state
				byte[] currentCol = {internalState[i*4], internalState[i*4+1], internalState[i*4+2], internalState[i*4+3]};
				for (int j=0; j < 4; j++) {	// row of constant matrix
					internalState[i*4 + j] = mulOneRowOneCol (mixColumnsG[j], currentCol);
				}
			}
		}

		for (int i = 0; i < 16; i++)
			internalState[i] = (byte) ((internalState[i] ^ roundKeyBytes[i]) & 0xFF);
	} // End of Rounds

	return internalState;
    }


    /**
     * NOTE: Xunhua Wang added this method on 05/27/2014
     * 		This textbook decryption requires the generation of the encryption in the reverse order. As a result, 
     *          one has to set the mode to be ENCRYPT_MODE or BOTH_MODE
     *
     * Convenience method to decrypt exactly one block of plaintext, assuming
     * Rijndael's default block size (128-bit).
     *
     * @param  in         The ciphertext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for decryption.
     * @return The plaintext generated from a ciphertext using the session key.
     */
    public static byte[]
    blockDecrypt (byte[] in, int inOffset, Object sessionKey) 
    {
	if (DEBUG) trace(IN, "blockDecrypt("+in+", "+inOffset+", "+sessionKey+")");
	/*
        int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
        int ROUNDS = Kd.length - 1;
        int[] Kdr = Kd[0];
	*/

        int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract DEcryption round keys
        int ROUNDS = Ke.length - 1;
        int[] Kdr = Ke[ROUNDS];

	byte[] internalState = new byte[16]; // XHW, 02/20/2014
	byte[] roundKeyBytes = new byte[16];
	int counter = 0;
	for (int i = 0; i < 4; i++) {
		roundKeyBytes[counter++] = (byte) (Kdr[i] >> 24 & 0xFF);
		roundKeyBytes[counter++] = (byte) (Kdr[i] >> 16 & 0xFF);
		roundKeyBytes[counter++] = (byte) (Kdr[i] >> 8 & 0xFF);
		roundKeyBytes[counter++] = (byte) (Kdr[i] & 0xFF);
	}

	for (int i = 0; i < 16; i++)
		internalState[i] = (byte) ((in[inOffset + i] ^ roundKeyBytes[i]) & 0xFF);

	for (int r = 1; r <= ROUNDS; r++) {          // apply round transforms
		// Kdr = Kd[r];
		Kdr = Ke [ROUNDS - r];
		counter = 0;
		for (int i = 0; i < 4; i++) {
			roundKeyBytes[counter++] = (byte) (Kdr[i] >> 24 & 0xFF);
			roundKeyBytes[counter++] = (byte) (Kdr[i] >> 16 & 0xFF);
			roundKeyBytes[counter++] = (byte) (Kdr[i] >> 8 & 0xFF);
			roundKeyBytes[counter++] = (byte) (Kdr[i] & 0xFF);
		}

		byte tmpByte = internalState[13];
		internalState[13] = internalState[9];
		internalState[9] = internalState[5];
		internalState[5] = internalState[1];
		internalState[1] = tmpByte;

		tmpByte = internalState[10];
		byte tmpByte2 = internalState[14];
		internalState[10] = internalState[2];
		internalState[14] = internalState[6];
		internalState[2] = tmpByte;
		internalState[6] = tmpByte2;

		tmpByte = internalState[3];
		internalState[3] = internalState[7];
		internalState[7] = internalState[11];
		internalState[11] = internalState[15];
		internalState[15] = tmpByte;

		for (int i = 0; i < 16; i++) 
			internalState[i] = Si[internalState[i] & 0xFF];

		for (int i = 0; i < 16; i++)
			internalState[i] = (byte) ((internalState[i] ^ roundKeyBytes[i]) & 0xFF);

		if (r != ROUNDS) {
			for (int i = 0; i < 4; i++) { // column of state
				byte[] currentCol = {internalState[i*4], internalState[i*4+1], internalState[i*4+2], internalState[i*4+3]};
				for (int j=0; j < 4; j++) {	// row of constant matrix
					internalState[i*4 + j] = mulOneRowOneCol (invMixColumnsG[j], currentCol);
				}
			}
		}

	} // End of Rounds

	return internalState;
    }

// Rijndael own methods
//...........................................................................

    /** @return The default length in bytes of the Algorithm input block. */
    public static int blockSize() { return BLOCK_SIZE; }

    /**
     * Expand a user-supplied key material into a session key.
     *
     * @param key        The 128/192/256-bit user-key to use.
     * @param blockSize  The block size in bytes of this Rijndael.
     * @exception  InvalidKeyException  If the key is invalid.
     */
    public static synchronized Object makeKey (int mode, byte[] k, int blockSize)
    throws InvalidKeyException 
    {
	if (DEBUG) trace(IN, "makeKey("+k+", "+blockSize+")");
        if (k == null)
            throw new InvalidKeyException("Empty key");
        if (!(k.length == 16 || k.length == 24 || k.length == 32))
             throw new InvalidKeyException("Incorrect key length");
        int ROUNDS = getRounds(k.length, blockSize);
        int BC = blockSize / 4;
        int[][] Ke = new int[ROUNDS + 1][BC]; // encryption round keys
        int[][] Kd = new int[ROUNDS + 1][BC]; // decryption round keys
        int ROUND_KEY_COUNT = (ROUNDS + 1) * BC;
        int KC = k.length / 4;
        int[] tk = new int[KC];
        int i, j;

        // copy user material bytes into temporary ints
        for (i = 0, j = 0; i < KC; )
            tk[i++] = (k[j++] & 0xFF) << 24 |
                      (k[j++] & 0xFF) << 16 |
                      (k[j++] & 0xFF) <<  8 |
                      (k[j++] & 0xFF);
        // copy values into round key arrays
        int t = 0;
        
        if (mode == ENCRYPT_MODE) {
            for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
                Ke[t / BC][t % BC] = tk[j];
            }
        } else if (mode == DECRYPT_MODE) {
            for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
                Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
            }
        } else {
            for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
                Ke[t / BC][t % BC] = tk[j];
                Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
            }
        }       
        
        int tt, rconpointer = 0;
        while (t < ROUND_KEY_COUNT) {
            // extrapolate using phi (the round key evolution function)
            tt = tk[KC - 1];
            tk[0] ^= (S[(tt >>> 16) & 0xFF] & 0xFF) << 24 ^
                     (S[(tt >>>  8) & 0xFF] & 0xFF) << 16 ^
                     (S[ tt         & 0xFF] & 0xFF) <<  8 ^
                     (S[(tt >>> 24) & 0xFF] & 0xFF)       ^
                     (rcon[rconpointer++]   & 0xFF) << 24;
            if (KC != 8)
                for (i = 1, j = 0; i < KC; ) tk[i++] ^= tk[j++];
            else {
                for (i = 1, j = 0; i < KC / 2; ) tk[i++] ^= tk[j++];
                tt = tk[KC / 2 - 1];
                tk[KC / 2] ^= (S[ tt         & 0xFF] & 0xFF)       ^
                              (S[(tt >>>  8) & 0xFF] & 0xFF) <<  8 ^
                              (S[(tt >>> 16) & 0xFF] & 0xFF) << 16 ^
                              (S[(tt >>> 24) & 0xFF] & 0xFF) << 24;
                for (j = KC / 2, i = j + 1; i < KC; ) tk[i++] ^= tk[j++];
            }
            // copy values into round key arrays
            if (mode == ENCRYPT_MODE) {
                for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
                    Ke[t / BC][t % BC] = tk[j];
                }
            } else if (mode == DECRYPT_MODE) {
                for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
                    Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
                }
            } else {
                for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
                    Ke[t / BC][t % BC] = tk[j];
                    Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
                }
            }
            
        }
        for (int r = 1; r < ROUNDS; r++)    // inverse MixColumn where needed
            for (j = 0; j < BC; j++) {
                tt = Kd[r][j];
                Kd[r][j] = U1[(tt >>> 24) & 0xFF] ^
                           U2[(tt >>> 16) & 0xFF] ^
                           U3[(tt >>>  8) & 0xFF] ^
                           U4[ tt         & 0xFF];
            }
        // assemble the encryption (Ke) and decryption (Kd) round keys into
        // one sessionKey object
        Object[] sessionKey = new Object[] {Ke, Kd};
	if (DEBUG) trace(OUT, "makeKey()");
        return sessionKey;
    }


 A SECOND IMPLEMENTATION \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    /**
     * Convenience method to encrypt exactly one block of plaintext, assuming
     * Rijndael's default block size (128-bit).
     *
     * @param  in         The plaintext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for encryption.
     * @return The ciphertext generated from a plaintext using the session key.
     */
    public static byte[]
    blockEncrypt2 (byte[] in, int inOffset, Object sessionKey)
    {
	if (DEBUG) trace(IN, "blockEncrypt2("+in+", "+inOffset+", "+sessionKey+")");
        int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
        int ROUNDS = Ke.length - 1;
        int[] Ker = Ke[0];

        // plaintext to ints + key
        int t0   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Ker[0];
        int t1   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Ker[1];
        int t2   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Ker[2];
        int t3   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Ker[3];

        int a0, a1, a2, a3;
        for (int r = 1; r < ROUNDS; r++) {          // apply round transforms
            Ker = Ke[r];
            a0   = (T1[(t0 >>> 24) & 0xFF] ^
                    T2[(t1 >>> 16) & 0xFF] ^
                    T3[(t2 >>>  8) & 0xFF] ^
                    T4[ t3         & 0xFF]  ) ^ Ker[0];
            a1   = (T1[(t1 >>> 24) & 0xFF] ^
                    T2[(t2 >>> 16) & 0xFF] ^
                    T3[(t3 >>>  8) & 0xFF] ^
                    T4[ t0         & 0xFF]  ) ^ Ker[1];
            a2   = (T1[(t2 >>> 24) & 0xFF] ^
                    T2[(t3 >>> 16) & 0xFF] ^
                    T3[(t0 >>>  8) & 0xFF] ^
                    T4[ t1         & 0xFF]  ) ^ Ker[2];
            a3   = (T1[(t3 >>> 24) & 0xFF] ^
                    T2[(t0 >>> 16) & 0xFF] ^
                    T3[(t1 >>>  8) & 0xFF] ^
                    T4[ t2         & 0xFF]  ) ^ Ker[3];
            t0 = a0;
            t1 = a1;
            t2 = a2;
            t3 = a3;
		if (DEBUG && debuglevel > 6) System.out.println("CT"+r+"="+intToString(t0)+intToString(t1)+intToString(t2)+intToString(t3));
        }

        // last round is special
        byte[] result = new byte[BLOCK_SIZE]; // the resulting ciphertext
        Ker = Ke[ROUNDS];
        int tt = Ker[0];
        result[ 0] = (byte)(S[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[ 1] = (byte)(S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[ 2] = (byte)(S[(t2 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[ 3] = (byte)(S[ t3         & 0xFF] ^  tt        );
        tt = Ker[1];
        result[ 4] = (byte)(S[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[ 5] = (byte)(S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[ 6] = (byte)(S[(t3 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[ 7] = (byte)(S[ t0         & 0xFF] ^  tt        );
        tt = Ker[2];
        result[ 8] = (byte)(S[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[ 9] = (byte)(S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[10] = (byte)(S[(t0 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[11] = (byte)(S[ t1         & 0xFF] ^  tt        );
        tt = Ker[3];
        result[12] = (byte)(S[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[13] = (byte)(S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[14] = (byte)(S[(t1 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[15] = (byte)(S[ t2         & 0xFF] ^  tt        );
	if (DEBUG && debuglevel > 6) {
		System.out.println("CT="+toString(result));
		System.out.println();
	}
	if (DEBUG) trace(OUT, "blockEncrypt2()");
        return result;
    }

    /**
     * Convenience method to decrypt exactly one block of plaintext, assuming
     * Rijndael's default block size (128-bit).
     *
     * @param  in         The ciphertext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for decryption.
     * @return The plaintext generated from a ciphertext using the session key.
     */
    public static byte[]
    blockDecrypt2 (byte[] in, int inOffset, Object sessionKey) 
    {
	if (DEBUG) trace(IN, "blockDecrypt2("+in+", "+inOffset+", "+sessionKey+")");
        int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
        int ROUNDS = Kd.length - 1;
        int[] Kdr = Kd[0];

        // ciphertext to ints + key
        int t0   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Kdr[0];
        int t1   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Kdr[1];
        int t2   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Kdr[2];
        int t3   = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Kdr[3];

        int a0, a1, a2, a3;
        for (int r = 1; r < ROUNDS; r++) {          // apply round transforms
            Kdr = Kd[r];
            a0   = (T5[(t0 >>> 24) & 0xFF] ^
                    T6[(t3 >>> 16) & 0xFF] ^
                    T7[(t2 >>>  8) & 0xFF] ^
                    T8[ t1         & 0xFF]  ) ^ Kdr[0];
            a1   = (T5[(t1 >>> 24) & 0xFF] ^
                    T6[(t0 >>> 16) & 0xFF] ^
                    T7[(t3 >>>  8) & 0xFF] ^
                    T8[ t2         & 0xFF]  ) ^ Kdr[1];
            a2   = (T5[(t2 >>> 24) & 0xFF] ^
                    T6[(t1 >>> 16) & 0xFF] ^
                    T7[(t0 >>>  8) & 0xFF] ^
                    T8[ t3         & 0xFF]  ) ^ Kdr[2];
            a3   = (T5[(t3 >>> 24) & 0xFF] ^
                    T6[(t2 >>> 16) & 0xFF] ^
                    T7[(t1 >>>  8) & 0xFF] ^
                    T8[ t0         & 0xFF]  ) ^ Kdr[3];
            t0 = a0;
            t1 = a1;
            t2 = a2;
            t3 = a3;
		if (DEBUG && debuglevel > 6) System.out.println("PT"+r+"="+intToString(t0)+intToString(t1)+intToString(t2)+intToString(t3));
        }

        // last round is special
        byte[] result = new byte[16]; // the resulting plaintext
        Kdr = Kd[ROUNDS];
        int tt = Kdr[0];
        result[ 0] = (byte)(Si[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[ 1] = (byte)(Si[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[ 2] = (byte)(Si[(t2 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[ 3] = (byte)(Si[ t1         & 0xFF] ^  tt        );
        tt = Kdr[1];
        result[ 4] = (byte)(Si[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[ 5] = (byte)(Si[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[ 6] = (byte)(Si[(t3 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[ 7] = (byte)(Si[ t2         & 0xFF] ^  tt        );
        tt = Kdr[2];
        result[ 8] = (byte)(Si[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[ 9] = (byte)(Si[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[10] = (byte)(Si[(t0 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[11] = (byte)(Si[ t3         & 0xFF] ^  tt        );
        tt = Kdr[3];
        result[12] = (byte)(Si[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
        result[13] = (byte)(Si[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
        result[14] = (byte)(Si[(t1 >>>  8) & 0xFF] ^ (tt >>>  8));
        result[15] = (byte)(Si[ t0         & 0xFF] ^  tt        );
	if (DEBUG && debuglevel > 6) {
		System.out.println("PT="+toString(result));
		System.out.println();
	}
	if (DEBUG) trace(OUT, "blockDecrypt2()");
        return result;
    }


 OTHER METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    /** A basic symmetric encryption/decryption test. */
    public static boolean self_test() { return self_test(BLOCK_SIZE); }


 ADDITIONAL METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

    /**
     * Encrypt exactly one block of plaintext.
     *
     * @param  in         The plaintext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for encryption.
     * @param  blockSize  The block size in bytes of this Rijndael.
     * @return The ciphertext generated from a plaintext using the session key.
     */
    public static byte[]
    blockEncrypt2 (byte[] in, int inOffset, Object sessionKey, int blockSize) {
        if (blockSize == BLOCK_SIZE)
            return blockEncrypt2(in, inOffset, sessionKey);
	if (DEBUG) trace(IN, "blockEncrypt2("+in+", "+inOffset+", "+sessionKey+", "+blockSize+")");
        Object[] sKey = (Object[]) sessionKey; // extract encryption round keys
        int[][] Ke = (int[][]) sKey[0];

        int BC = blockSize / 4;
        int ROUNDS = Ke.length - 1;
        int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2);
        int s1 = shifts[SC][1][0];
        int s2 = shifts[SC][2][0];
        int s3 = shifts[SC][3][0];
        int[] a = new int[BC];
        int[] t = new int[BC]; // temporary work array
        int i;
        byte[] result = new byte[blockSize]; // the resulting ciphertext
        int j = 0, tt;

        for (i = 0; i < BC; i++)                   // plaintext to ints + key
            t[i] = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Ke[0][i];
        for (int r = 1; r < ROUNDS; r++) {          // apply round transforms
            for (i = 0; i < BC; i++)
                a[i] = (T1[(t[ i           ] >>> 24) & 0xFF] ^
                        T2[(t[(i + s1) % BC] >>> 16) & 0xFF] ^
                        T3[(t[(i + s2) % BC] >>>  8) & 0xFF] ^
                        T4[ t[(i + s3) % BC]         & 0xFF]  ) ^ Ke[r][i];
            System.arraycopy(a, 0, t, 0, BC);
		if (DEBUG && debuglevel > 6) System.out.println("CT"+r+"="+toString(t));
        }
        for (i = 0; i < BC; i++) {                   // last round is special
            tt = Ke[ROUNDS][i];
            result[j++] = (byte)(S[(t[ i           ] >>> 24) & 0xFF] ^ (tt >>> 24));
            result[j++] = (byte)(S[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ (tt >>> 16));
            result[j++] = (byte)(S[(t[(i + s2) % BC] >>>  8) & 0xFF] ^ (tt >>>  8));
            result[j++] = (byte)(S[ t[(i + s3) % BC]         & 0xFF] ^ tt);
        }
	if (DEBUG && debuglevel > 6) {
		System.out.println("CT="+toString(result));
		System.out.println();
	}
	if (DEBUG) trace(OUT, "blockEncrypt2()");
        return result;
    }

    /**
     * Decrypt exactly one block of ciphertext.
     *
     * @param  in         The ciphertext.
     * @param  inOffset   Index of in from which to start considering data.
     * @param  sessionKey The session key to use for decryption.
     * @param  blockSize  The block size in bytes of this Rijndael.
     * @return The plaintext generated from a ciphertext using the session key.
     */
    public static byte[]
    blockDecrypt2 (byte[] in, int inOffset, Object sessionKey, int blockSize) 
    {
        if (blockSize == BLOCK_SIZE)
            return blockDecrypt2 (in, inOffset, sessionKey);
	if (DEBUG) trace(IN, "blockDecrypt2("+in+", "+inOffset+", "+sessionKey+", "+blockSize+")");
        Object[] sKey = (Object[]) sessionKey; // extract decryption round keys
        int[][] Kd = (int[][]) sKey[1];

        int BC = blockSize / 4;
        int ROUNDS = Kd.length - 1;
        int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2);
        int s1 = shifts[SC][1][1];
        int s2 = shifts[SC][2][1];
        int s3 = shifts[SC][3][1];
        int[] a = new int[BC];
        int[] t = new int[BC]; // temporary work array
        int i;
        byte[] result = new byte[blockSize]; // the resulting plaintext
        int j = 0, tt;

        for (i = 0; i < BC; i++)                   // ciphertext to ints + key
            t[i] = ((in[inOffset++] & 0xFF) << 24 |
                    (in[inOffset++] & 0xFF) << 16 |
                    (in[inOffset++] & 0xFF) <<  8 |
                    (in[inOffset++] & 0xFF)        ) ^ Kd[0][i];
        for (int r = 1; r < ROUNDS; r++) {          // apply round transforms
            for (i = 0; i < BC; i++)
                a[i] = (T5[(t[ i           ] >>> 24) & 0xFF] ^
                        T6[(t[(i + s1) % BC] >>> 16) & 0xFF] ^
                        T7[(t[(i + s2) % BC] >>>  8) & 0xFF] ^
                        T8[ t[(i + s3) % BC]         & 0xFF]  ) ^ Kd[r][i];
            System.arraycopy(a, 0, t, 0, BC);
		if (DEBUG && debuglevel > 6) System.out.println("PT"+r+"="+toString(t));
        }
        for (i = 0; i < BC; i++) {                   // last round is special
            tt = Kd[ROUNDS][i];
            result[j++] = (byte)(Si[(t[ i           ] >>> 24) & 0xFF] ^ (tt >>> 24));
            result[j++] = (byte)(Si[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ (tt >>> 16));
            result[j++] = (byte)(Si[(t[(i + s2) % BC] >>>  8) & 0xFF] ^ (tt >>>  8));
            result[j++] = (byte)(Si[ t[(i + s3) % BC]         & 0xFF] ^ tt);
        }
	if (DEBUG && debuglevel > 6) {
		System.out.println("PT="+toString(result));
		System.out.println();
	}
	if (DEBUG) trace(OUT, "blockDecrypt2()");
        return result;
    }

    /** A basic symmetric encryption/decryption test for a given key size. */
    private static boolean self_test (int keysize) 
    {
	if (DEBUG) trace(IN, "self_test("+keysize+")");
        boolean ok = false;
        try {
            byte[] kb = new byte[keysize];
            byte[] pt = new byte[BLOCK_SIZE];
            int i;

            for (i = 0; i < keysize; i++)
                kb[i] = (byte) i;
            for (i = 0; i < BLOCK_SIZE; i++)
                pt[i] = (byte) i;

		if (DEBUG && debuglevel > 6) {
			System.out.println("==========");
			System.out.println();
			System.out.println("KEYSIZE="+(8*keysize));
			System.out.println("KEY="+toString(kb));
			System.out.println();
		}
            Object key = makeKey(BOTH_MODE, kb, BLOCK_SIZE);

		if (DEBUG && debuglevel > 6) {
			System.out.println("Intermediate Ciphertext Values (Encryption)");
			System.out.println();
			System.out.println("PT="+toString(pt));
		}
            byte[] ct =  blockEncrypt2(pt, 0, key, BLOCK_SIZE);

		if (DEBUG && debuglevel > 6) {
			System.out.println("Intermediate Plaintext Values (Decryption)");
			System.out.println();
			System.out.println("CT="+toString(ct));
		}
            byte[] cpt = blockDecrypt2(ct, 0, key, BLOCK_SIZE);

            ok = areEqual(pt, cpt);
            if (!ok)
                throw new RuntimeException("Symmetric operation failed");
        }
        catch (Exception x) {
		if (DEBUG && debuglevel > 0) {
    			debug("Exception encountered during self-test: " + x.getMessage());
    			x.printStackTrace();
		}
        }
	if (DEBUG && debuglevel > 0) debug("Self-test OK? " + ok);
	if (DEBUG) trace(OUT, "self_test()");
        return ok;
    }

    /**
     * Return The number of rounds for a given Rijndael's key and block sizes.
     *
     * @param keySize    The size of the user key material in bytes.
     * @param blockSize  The desired block size in bytes.
     * @return The number of rounds for a given Rijndael's key and
     *      block sizes.
     */
    public static int getRounds (int keySize, int blockSize) {
        switch (keySize) {
        case 16:
            return blockSize == 16 ? 10 : (blockSize == 24 ? 12 : 14);
        case 24:
            return blockSize != 32 ? 12 : 14;
        default: // 32 bytes = 256 bits
            return 14;
        }
    }


 UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
//
// utility static methods (from cryptix.util.core ArrayUtil and Hex classes)
//...........................................................................

    /**
     * Compares two byte arrays for equality.
     *
     * @return true if the arrays have identical contents
     */
    private static boolean areEqual (byte[] a, byte[] b) {
        int aLength = a.length;
        if (aLength != b.length)
            return false;
        for (int i = 0; i < aLength; i++)
            if (a[i] != b[i])
                return false;
        return true;
    }

    /**
     * Returns a string of 2 hexadecimal digits (most significant
     * digit first) corresponding to the lowest 8 bits of <i>n</i>.
     */
    private static String byteToString (int n) {
        char[] buf = {
            HEX_DIGITS[(n >>> 4) & 0x0F],
            HEX_DIGITS[ n        & 0x0F]
        };
        return new String(buf);
    }

    /**
     * Returns a string of 8 hexadecimal digits (most significant
     * digit first) corresponding to the integer <i>n</i>, which is
     * treated as unsigned.
     */
    private static String intToString (int n) {
        char[] buf = new char[8];
        for (int i = 7; i >= 0; i--) {
            buf[i] = HEX_DIGITS[n & 0x0F];
            n >>>= 4;
        }
        return new String(buf);
    }

    /**
     * Returns a string of hexadecimal digits from a byte array. Each
     * byte is converted to 2 hex symbols.
     */
    private static String toString (byte[] ba) {
        int length = ba.length;
        char[] buf = new char[length * 2];
        for (int i = 0, j = 0, k; i < length; ) {
            k = ba[i++];
            buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
            buf[j++] = HEX_DIGITS[ k        & 0x0F];
        }
        return new String(buf);
    }

//
// Xunhua: We need to add some spaces and line breaks
//
    private static String toString2 (byte[] ba) {
        int length = ba.length;
        // char[] buf = new char[length * 2];
	char[] buf2 = new char[2];
	StringBuffer sb = new StringBuffer ();
        for (int i = 0; i < length; ) {
            int k = ba[i++];
            buf2[0] = HEX_DIGITS[(k >>> 4) & 0x0F];
            buf2[1] = HEX_DIGITS[ k        & 0x0F];
		sb.append (buf2);

		if (i%16 == 0) {
			sb.append (System.getProperty ("line.separator"));
		} else {
			sb.append (" ");
		}

        }
        return sb.toString();
    }

    /**
     * Returns a string of hexadecimal digits from an integer array. Each
     * int is converted to 4 hex symbols.
     */
    private static String toString (int[] ia) {
        int length = ia.length;
        char[] buf = new char[length * 8];
        for (int i = 0, j = 0, k; i < length; i++) {
            k = ia[i];
            buf[j++] = HEX_DIGITS[(k >>> 28) & 0x0F];
            buf[j++] = HEX_DIGITS[(k >>> 24) & 0x0F];
            buf[j++] = HEX_DIGITS[(k >>> 20) & 0x0F];
            buf[j++] = HEX_DIGITS[(k >>> 16) & 0x0F];
            buf[j++] = HEX_DIGITS[(k >>> 12) & 0x0F];
            buf[j++] = HEX_DIGITS[(k >>>  8) & 0x0F];
            buf[j++] = HEX_DIGITS[(k >>>  4) & 0x0F];
            buf[j++] = HEX_DIGITS[ k         & 0x0F];
        }
        return new String(buf);
    }

    private static String toString2 (int[] ia) {
        int length = ia.length;
        // char[] buf = new char[length * 8];
        char[] buf2 = new char[8];
	StringBuffer sb = new StringBuffer ();

        for (int i = 0; i < length; i++) {

		if (i != 0 && i%16 == 0) {
			sb.append (System.getProperty ("line.separator"));
		} else if (i != 0) {
			sb.append (" ");
		}

            int k = ia[i];
            buf2[0] = HEX_DIGITS[(k >>> 28) & 0x0F];
            buf2[1] = HEX_DIGITS[(k >>> 24) & 0x0F];
            buf2[2] = HEX_DIGITS[(k >>> 20) & 0x0F];
            buf2[3] = HEX_DIGITS[(k >>> 16) & 0x0F];
            buf2[4] = HEX_DIGITS[(k >>> 12) & 0x0F];
            buf2[5] = HEX_DIGITS[(k >>>  8) & 0x0F];
            buf2[6] = HEX_DIGITS[(k >>>  4) & 0x0F];
            buf2[7] = HEX_DIGITS[ k         & 0x0F];

	    sb.append (buf2);

        }
        return sb.toString();
    }

	// main(): use to generate the Intermediate Values KAT
	//...........................................................................
	public static void main (String[] args) {
		self_test(16);
		self_test(24);
		self_test(32);
	}
}

// $Id: Rijndael_Properties.java,v 1.1.1.1 2005/10/11 16:49:07 ruthap Exp $
//
// $Log: Rijndael_Properties.java,v $
// Revision 1.1.1.1  2005/10/11 16:49:07  ruthap
// My Research Source Code
//
// Revision 1.0  1998/04/07  raif
// + original version.
//
// $Endlog$
/*
 * Copyright (c) 1997, 1998 Systemics Ltd on behalf of
 * the Cryptix Development Team. All rights reserved.
 */
// package Rijndael;

//import java.io.FileInputStream;
//import java.io.FileNotFoundException;
import java.io.InputStream;
//import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Properties;

/**
 * This class acts as a central repository for an algorithm specific
 * properties. It reads an (algorithm).properties file containing algorithm-
 * specific properties. When using the AES-Kit, this (algorithm).properties
 * file is located in the (algorithm).jar file produced by the "jarit" batch/
 * script command.<p>
 *
 * <b>Copyright</b> &copy; 1997, 1998
 * <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
 * <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
 * <br>All rights reserved.<p>
 *
 * <b>$Revision: 1.1.1.1 $</b>
 * @author  David Hopwood
 * @author  Jill Baker
 * @author  Raif S. Naffah
 */
public class Rijndael_Properties // implicit no-argument constructor
{
// Constants and variables with relevant static code
//...........................................................................

    static final boolean GLOBAL_DEBUG = false;

    static final String ALGORITHM = "Rijndael";
    static final double VERSION = 0.1;
    static final String FULL_NAME = ALGORITHM + " ver. " + VERSION;
    static final String NAME = "Rijndael_Properties";

    static final Properties properties = new Properties();

    /** Default properties in case .properties file was not found. */
    private static final String[][] DEFAULT_PROPERTIES = {
        {"Trace.Rijndael_Algorithm",       "true"},
        {"Debug.Level.*",                  "1"},
        {"Debug.Level.Rijndael_Algorithm", "9"},
    };

    static {
if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Looking for " + ALGORITHM + " properties");
        String it = ALGORITHM + ".properties";
        InputStream is = Rijndael_Properties.class.getResourceAsStream(it);
        boolean ok = is != null;
        if (ok)
            try {
                properties.load(is);
                is.close();
if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Properties file loaded OK...");
            } catch (Exception x) {
                ok = false;
            }
        if (!ok) {
if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": WARNING: Unable to load \"" + it + "\" from CLASSPATH.");
if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ":          Will use default values instead...");
            int n = DEFAULT_PROPERTIES.length;
            for (int i = 0; i < n; i++)
                properties.put(
                    DEFAULT_PROPERTIES[i][0], DEFAULT_PROPERTIES[i][1]);
if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Default properties now set...");
        }
    }


// Properties methods (excluding load and save, which are deliberately not
// supported).
//...........................................................................

    /** Get the value of a property for this algorithm. */
    public static String getProperty (String key) {
        return properties.getProperty(key);
    }

    /**
     * Get the value of a property for this algorithm, or return
     * <i>value</i> if the property was not set.
     */
    public static String getProperty (String key, String value) {
        return properties.getProperty(key, value);
    }

    /** List algorithm properties to the PrintStream <i>out</i>. */
    public static void list (PrintStream out) {
        list(new PrintWriter(out, true));
    }

    /** List algorithm properties to the PrintWriter <i>out</i>. */
    public static void list (PrintWriter out) {
        out.println("#");
        out.println("# ----- Begin "+ALGORITHM+" properties -----");
        out.println("#");
        String key, value;
        Enumeration enum1 = properties.propertyNames();
        while (enum1.hasMoreElements()) {
            key = (String) enum1.nextElement();
            value = getProperty(key);
            out.println(key + " = " + value);
        }
        out.println("#");
        out.println("# ----- End "+ALGORITHM+" properties -----");
    }

//    public synchronized void load(InputStream in) throws IOException {}

    public static Enumeration propertyNames() {
        return properties.propertyNames();
    }

//    public void save (OutputStream os, String comment) {}


// Developer support: Tracing and debugging enquiry methods (package-private)
//...........................................................................
    
    /**
     * Return true if tracing is requested for a given class.<p>
     *
     * User indicates this by setting the tracing <code>boolean</code>
     * property for <i>label</i> in the <code>(algorithm).properties</code>
     * file. The property's key is "<code>Trace.<i>label</i></code>".<p>
     *
     * @param label  The name of a class.
     * @return True iff a boolean true value is set for a property with
     *      the key <code>Trace.<i>label</i></code>.
     */
    static boolean isTraceable (String label) {
        String s = getProperty("Trace." + label);
        if (s == null)
            return false;
        return new Boolean(s).booleanValue();
    }

    /**
     * Return the debug level for a given class.<p>
     *
     * User indicates this by setting the numeric property with key
     * "<code>Debug.Level.<i>label</i></code>".<p>
     *
     * If this property is not set, "<code>Debug.Level.*</code>" is looked up
     * next. If neither property is set, or if the first property found is
     * not a valid decimal integer, then this method returns 0.
     *
     * @param label  The name of a class.
     * @return  The required debugging level for the designated class.
     */
    static int getLevel(String label) {
        String s = getProperty("Debug.Level." + label);
        if (s == null) {
            s = getProperty("Debug.Level.*");
            if (s == null)
                return 0;
        }
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    /**
     * Return the PrintWriter to which tracing and debugging output is to
     * be sent.<p>
     *
     * User indicates this by setting the property with key <code>Output</code>
     * to the literal <code>out</code> or <code>err</code>.<p>
     *
     * By default or if the set value is not allowed, <code>System.err</code>
     * will be used.
     */
    static PrintWriter getOutput() {
        PrintWriter pw;
        String name = getProperty("Output");
        if (name != null && name.equals("out"))
            pw = new PrintWriter(System.out, true);
        else
            pw = new PrintWriter(System.err, true);
        return pw;
    }
}
Error: Could not find or load main class AES_NEW
Error: Could not find or load main class AES_NEW.java
Error: Could not find or load main class AES_NEW
Error: Could not find or load main class AES_NEW
Error: Could not find or load main class AES_NEW
Error: Could not find or load main class AES_NEW
Error: Could not find or load main class AES_NEW
Error: Could not find or load main class AES_NEW
Where to start?
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:907)
at java.util.Scanner.next(Scanner.java:1530)
at java.util.Scanner.nextInt(Scanner.java:2160)
at java.util.Scanner.nextInt(Scanner.java:2119)
at AES_NEW.main(AES_NEW.java:26)
Where to start?
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:907)
at java.util.Scanner.next(Scanner.java:1530)
at java.util.Scanner.nextInt(Scanner.java:2160)
at java.util.Scanner.nextInt(Scanner.java:2119)
at AES_NEW.main(AES_NEW.java:26)
Where to start?
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:907)
at java.util.Scanner.next(Scanner.java:1530)
at java.util.Scanner.nextInt(Scanner.java:2160)
at java.util.Scanner.nextInt(Scanner.java:2119)
at AES_NEW.main(AES_NEW.java:26)
Where to start?
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:907)
at java.util.Scanner.next(Scanner.java:1530)
at java.util.Scanner.nextInt(Scanner.java:2160)
at java.util.Scanner.nextInt(Scanner.java:2119)
at AES_NEW.main(AES_NEW.java:26)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值