AES Java Implementation

From Second Life Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Description

The following is a simple Java example of AES encryption and decryption, compatible with the LSL AES Engine by Haravikk Mistral.

Required Classes

Base64Coder

package lslAESCrypto;

/**
 * A Base64 Encoder/Decoder.
 * <p>
 * This class is used to encode and decode data in Base64 format as described in
 * RFC 1521.
 * </p>
 */
public class Base64Coder {
	/** Mapping table from 6-bits to Base64 characters. */
	private static char[] BITS_TO_BASE64_CHAR =
		           { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
			'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
			'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
			'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
			'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+',
			'/' };

	/** Mapping table from Base64 characters to 6-bits. */
	private static byte[] BASE64_CHAR_TO_BITS =
		          { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
			-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
			52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1,
			0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
			19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
			30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
			47, 48, 49, 50, 51, -1, -1, -1, -1, -1, };

	/**
	 * Decodes a byte array from Base64 format. No blanks or line breaks are
	 * allowed within the Base64 encoded data.
	 * 
	 * @param in
	 *            a character array containing the Base64 encoded data.
	 * @return An array containing the decoded data bytes.
	 * @throws IllegalArgumentException
	 *             if the input is not valid Base64 encoded data.
	 */
	public static byte[] decode(final char[] in)
		throws IllegalArgumentException {
		int len = in.length;
		if (len % 4 != 0)
			throw new IllegalArgumentException(
				"Length of Base64 encoded input string is not a multiple of 4.");

		// Ignore trailing equals
		while (len > 0 && in[len - 1] == '=')
			--len;

		final byte[] bytes = new byte[(len * 3) / 4];
		int o = 0;

		for (int i = 0; i < len;) {
			try {
				final char c0 = in[i++];
				final char c1 = in[i++];
				final char c2 = (i < len) ? in[i++] : 'A';
				final char c3 = (i < len) ? in[i++] : 'A';

				if (c0 > 127 || c1 > 127 || c2 > 127 || c3 > 127)
					throw new IllegalArgumentException(
						"Invalid base64 character");

				final byte b0 = Base64Coder.BASE64_CHAR_TO_BITS[c0];
				final byte b1 = Base64Coder.BASE64_CHAR_TO_BITS[c1];
				final byte b2 = Base64Coder.BASE64_CHAR_TO_BITS[c2];
				final byte b3 = Base64Coder.BASE64_CHAR_TO_BITS[c3];

				if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
					throw new IllegalArgumentException(
						"Invalid base64 character");

				bytes[o++] = (byte) ((b0 << 2) | (b1 >>> 4));
				if (o < bytes.length) {
					bytes[o++] = (byte) (((b1 & 0xF) << 4) | (b2 >>> 2));
					if (o < bytes.length)
						bytes[o++] = (byte) (((b2 & 0x3) << 6) | b3);
				}
			} catch (final ArrayIndexOutOfBoundsException e) {
				throw new IllegalArgumentException("Invalid base64 character");
			}
		}

		return bytes;
	}

	/**
	 * Decodes a byte array from Base64 format.
	 * 
	 * @param s
	 *            a Base64 String to be decoded.
	 * @return An array containing the decoded data bytes.
	 * @throws IllegalArgumentException
	 *             if the input is not valid Base64 encoded data.
	 */
	public static byte[] decode(final String s) {
		return Base64Coder.decode(s.toCharArray());
	}

	/**
	 * Decodes a string from Base64 format.
	 * 
	 * @param s
	 *            a Base64 String to be decoded.
	 * @return A String containing the decoded data.
	 * @throws IllegalArgumentException
	 *             if the input is not valid Base64 encoded data.
	 */
	public static String decodeString(final String s) {
		return new String(Base64Coder.decode(s));
	}

	/**
	 * Encodes a byte array into Base64 format. No blanks or line breaks are
	 * inserted.
	 * 
	 * @param in
	 *            an array containing the data bytes to be encoded.
	 * @return A character array with the Base64 encoded data.
	 */
	public static char[] encode(final byte[] in) {
		return Base64Coder.encode(in, 0, in.length);
	}

	/**
	 * Encodes a byte array into Base64 format. No blanks or line breaks are
	 * inserted.
	 * 
	 * @param in
	 *            an array containing the data bytes to be encoded.
	 * @param offset
	 *            the offset into the array at which to begin reading.
	 * @param bits
	 *            number of <b>bits</b> to process from <code>in</code>.
	 * @return A character array with the Base64 encoded data.
	 */
	public static char[] encode(
		final byte[] in,
		final int offset,
		final int bits) {
		int length = bits / 8;
		if ((length * 8) < bits) ++length;

		final char[] chars = new char[((length + 2) / 3) * 4];
		final int out = ((length * 4) + 2) / 3;

		int mask = ~(-1 << (8 - (bits % 8))) | ~(-1 << (bits % 8));
		if (mask == 0) mask = 0xFF;

		int o = 0;
		final int end = length + offset;
		for (int i = offset; i < end;) {
			final int b0 = ((i + 1) == end) ? in[i++] & mask : in[i++] & 0xFF;
			final int b1 =
				(i < length) ? (((i + 1) == end) ? in[i++] & mask
					: in[i++] & 0xFF) : 0;
			final int b2 =
				(i < length) ? (((i + 1) == end) ? in[i++] & mask
					: in[i++] & 0xFF) : 0;

			final int i0 = (b0 >>> 2);
			final int i1 = (((b0 & 0x3) << 4) | (b1 >>> 4));
			final int i2 = (((b1 & 0xF) << 2) | (b2 >>> 6));
			final int i3 = b2 & 0x3F;

			chars[o++] = Base64Coder.BITS_TO_BASE64_CHAR[i0];
			chars[o++] = Base64Coder.BITS_TO_BASE64_CHAR[i1];
			chars[o] = (o < out) ? Base64Coder.BITS_TO_BASE64_CHAR[i2] : '=';
			++o;
			chars[o] = (o < out) ? Base64Coder.BITS_TO_BASE64_CHAR[i3] : '=';
			++o;
		}

		return chars;
	}

	/**
	 * Produces a base64 string from the provided byte-array.
	 * 
	 * @param bytes
	 *            the byte-array to read-from.
	 * @return the base64 encoded string produced.
	 */
	public static String encodeString(final byte[] bytes) {
		return Base64Coder.encodeString(bytes, 0, bytes.length);
	}

	/**
	 * Produces a base64 string from the provided byte-array slice.
	 * 
	 * @param bytes
	 *            the byte-array to read-from.
	 * @param offset
	 *            the offset into the array at which to begin reading.
	 * @param bits
	 *            number of <b>bits</b> to process from <code>bytes</code>.
	 * @return the base64 encoded string produced.
	 */
	public static String encodeString(
		final byte[] bytes,
		final int offset,
		final int bits) {
		return new String(Base64Coder.encode(bytes, offset, bits));
	}

	/**
	 * Encodes a string into Base64 format. No blanks or line breaks are
	 * inserted.
	 * 
	 * @param s
	 *            a String to be encoded.
	 * @return A String with the Base64 encoded data.
	 */
	public static String encodeString(final String s) {
		return new String(Base64Coder.encode(s.getBytes()));
	}

	/** Dummy constructor. */
	private Base64Coder() { /* Blocking constructor */}

}

HexCoder

package lslAESCrypto;
/**
 * The following is a simple set of static methods for converting from hex to
 * bytes and vice-versa
 * 
 * @author Haravikk Mistral
 * @date Sep 15, 2008, 3:26:42 PM
 * @version 1.0
 */
public class HexCoder {
	/**
	 * Quick converts bytes to hex-characters
	 * 
	 * @param bytes
	 *            the byte-array to convert
	 * @return the hex-representation
	 */
	public static String bytesToHex(final byte[] bytes) {
		final StringBuffer s = new StringBuffer(bytes.length * 2);
		for (int i = 0; i < bytes.length; ++i) {
			s.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16));
			s.append(Character.forDigit(bytes[i] & 0xF, 16));
		}
		return s.toString();
	}

	/**
	 * Quickly converts hex-characters to bytes
	 * 
	 * @param s
	 *            the hex-string
	 * @return the bytes represented
	 */
	public static byte[] hexToBytes(final String s) {
		final byte[] bytes = new byte[s.length() / 2];
		for (int i = 0; i < bytes.length; ++i)
			bytes[i] = (byte) Integer.parseInt(
				s.substring(2 * i, (2 * i) + 2),
				16);
		return bytes;
	}
}

Class

package lslAESCrypto;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * <p>
 * This is a class designed to process AES messages sent from the LSL
 * implementation of AES which can be found here:<br/>
 * <a
 * href="https://wiki.secondlife.com/wiki/AES_LSL_Implementation">https://wiki
 * .secondlife.com/wiki/AES_LSL_Implementation</a>
 * </p>
 * <p>
 * This Java class will be updated to support the same modes of operation as the
 * LSL implementation. It currently assumes that keys and input-vectors are
 * processed as hex-strings, and that text is received as plain-text, while
 * ciphertext will be handled as base64 strings.
 * </p>
 * 
 * @author Haravikk
 * @date Sep 15, 2008, 4:18:48 PM
 * @version 1.0
 */
public class LSLAESCrypto {
	/** Our currently set block-cipher mode */
	protected LSLAESCryptoMode mode = LSLAESCryptoMode.CBC;
	/** Used to detect when a new {@link Cipher} Is needed. */
	protected boolean modeChanged = false;

	/** Our currently set padding mode */
	protected LSLAESCryptoPad pad = LSLAESCryptoPad.NONE;
	/** Our currently set pad-size */
	protected int padSize = 512;

	/** The currently loaded key */
	protected SecretKeySpec keySpec = null;
	/** The currently loaded input-vector */
	protected IvParameterSpec ivSpec = null;

	/** The currently active cipher */
	protected Cipher cipher = null;

	/** A random class for secure random operations. */
	protected Random random = new SecureRandom();

	/**
	 * Creates an instance of an LSL compatible AES handler.
	 * 
	 * @param mode
	 *            the cipher-block mode of operation
	 * @param pad
	 *            the padding scheme to use
	 * @param padSize
	 *            the block-size to use when padding. Must be a non-zero,
	 *            positive value that is a multiple of 128.
	 * @param hexKey
	 *            the key to start with (represented as hexadecimal string)
	 * @param hexIV
	 *            the input vector to start with (represented as hexadecimal
	 *            string)
	 * @throws NoSuchAlgorithmException
	 *             if the AES algorithm is not supported by the current JVM
	 * @throws NoSuchPaddingException
	 *             if the padding scheme chosen is not supported by the current
	 *             JVM
	 */
	public LSLAESCrypto(
		final LSLAESCryptoMode mode,
			final LSLAESCryptoPad pad,
			final int padSize,
			final String hexKey,
			final String hexIV)
		throws NoSuchAlgorithmException,
			NoSuchPaddingException {
		this.init(mode, pad, padSize, hexKey, hexIV);
	}

	/**
	 * Decrypts a base64 ciphertext into plain-text
	 * 
	 * @param base64ciphertext
	 *            the ciphertext to decrypt
	 * @return the plain-text that was originally encrypted
	 * @throws InvalidKeyException
	 *             if the currently loaded key is not valid
	 * @throws InvalidAlgorithmParameterException
	 *             if the AES algorithm is not supported by the current JVM
	 * @throws IllegalBlockSizeException
	 *             if the ciphertext is somehow unreadable (bad base64
	 *             conversion)
	 * @throws BadPaddingException
	 *             if the chosen mode of operation requires padded data
	 */
	public String decrypt(final String base64ciphertext)
		throws InvalidKeyException,
			InvalidAlgorithmParameterException,
			IllegalBlockSizeException,
			BadPaddingException {
		if (this.modeChanged) try {
			this.createCipher();
		} catch (final Exception e) { /* Do nothing */}

		this.cipher.init(Cipher.DECRYPT_MODE, this.keySpec, this.ivSpec);
		return new String(this.cipher.doFinal(Base64Coder
			.decode(base64ciphertext)));
	}

	/**
	 * Encrypts plain-text into a base64 string
	 * 
	 * @param text
	 *            the plain-text to encrypt
	 * @return the base64 ciphertext produced
	 * @throws IllegalBlockSizeException
	 *             if the plain text is somehow invalid
	 * @throws BadPaddingException
	 *             if the chosen mode of operation requires padded data
	 * @throws InvalidKeyException
	 *             if the currently loaded key is invalid
	 * @throws InvalidAlgorithmParameterException
	 *             if the AES algorithm is not supported by the current JVM
	 */
	public String encrypt(final String text)
		throws IllegalBlockSizeException,
			BadPaddingException,
			InvalidKeyException,
			InvalidAlgorithmParameterException {
		if (this.modeChanged) try {
			this.createCipher();
		} catch (final Exception e) { /* Do nothing */}

		this.cipher.init(Cipher.ENCRYPT_MODE, this.keySpec, this.ivSpec);

		byte[] data = text.getBytes();
		int bits = data.length * 8;

		/* Apply padding */
		LSLAESCryptoPad padding = this.pad;
		if (padding == LSLAESCryptoPad.NONE) {
			if (this.mode == LSLAESCryptoMode.CFB) { return Base64Coder
				.encodeString(this.cipher.doFinal(data), 0, bits); }
			padding = LSLAESCryptoPad.RBT;
		}

		int blockSize = this.padSize;
		if (padding == LSLAESCryptoPad.RBT) blockSize = 128;

		final int blocks = bits / blockSize;
		int extra = bits % blockSize;

		if (padding == LSLAESCryptoPad.RBT) {
			if (extra > 0) {
				/*
				 * This scheme takes the last encrypted block, encrypts it
				 * again, and XORs it with any leftover data, maintaining
				 * data-length. If input is less than a block in size, then the
				 * current input-vector is used.
				 */
				int bytes = extra / 8;
				if ((bytes * 8) < extra) ++bytes;

				// Grab leftover bytes
				final byte[] t = new byte[bytes];
				if (bytes > 0)
					System.arraycopy(data, data.length - bytes, t, 0, bytes);

				// Encrypt all other data.
				byte[] lb;
				if (blocks < 1) {
					// If not enough for a block, double-encrypt IV.
					data = new byte[0];
					lb =
						this.cipher.doFinal(this.cipher.doFinal(this.ivSpec
							.getIV()));
				} else {
					// If there are blocks, then double-encrypt final one.
					data = this.cipher.doFinal(data, 0, data.length - bytes);
					lb = this.cipher.doFinal(data, data.length - 16, 16);
				}

				// XOR lb with t.
				for (int i = 0; i < t.length; ++i)
					t[i] ^= lb[i];

				lb = new byte[data.length + t.length];
				System.arraycopy(data, 0, lb, 0, data.length);
				System.arraycopy(t, 0, lb, data.length, t.length);

				return Base64Coder.encodeString(lb);
			}
			return Base64Coder.encodeString(this.cipher.doFinal(data), 0, bits);
		}

		// Padding schemes that add bytes until block-boundary is reached.
		extra = blockSize - extra;

		if (padding == LSLAESCryptoPad.NULLS_SAFE) {
			++bits;
			final int bytes = bits / 8;
			final int bit = bytes % 8;

			if (bytes < data.length) data[bytes] |= (1 << (8 - bit));
			else {
				final byte[] t = new byte[data.length + 1];
				System.arraycopy(data, 0, t, 0, data.length);
				t[data.length] = (byte) 0x80;
				data = t;
			}

			if ((--extra) < 0) extra += blockSize;
			padding = LSLAESCryptoPad.NULLS;
		}

		int bytes = extra / 8;
		if (bytes <= 0) {
			if (padding == LSLAESCryptoPad.NULLS)
				return Base64Coder.encodeString(
					this.cipher.doFinal(data),
					0,
					bits);

			bytes = blockSize / 8;
			extra += blockSize;
		}

		bits += extra;
		final byte[] t = new byte[data.length + bytes];
		int i = data.length;
		System.arraycopy(data, 0, t, 0, data.length);
		data = t;

		for (; i < data.length; ++i) {
			byte b = 0;
			if ((i >= (data.length - 4)) && (padding != LSLAESCryptoPad.NULLS)) b =
				(byte) bytes;
			else if (padding == LSLAESCryptoPad.RANDOM)
				b = (byte) this.random.nextInt(256);

			data[i] = b;
		}

		return Base64Coder.encodeString(this.cipher.doFinal(data), 0, bits);
	}

	/**
	 * Initialises this AES instance with a mode, pad, key, and input vector in
	 * a single operation
	 * 
	 * @param mode
	 *            the cipher-block mode of operation
	 * @param pad
	 *            the padding scheme to use
	 * @param padSize
	 *            the block-size to use when padding. Must be a non-zero,
	 *            positive value that is a multiple of 128.
	 * @param hexKey
	 *            the key to use as a hexadecimal string
	 * @param hexIV
	 *            the input-vector to use as a hexadecimal string
	 * @throws NoSuchAlgorithmException
	 *             if the AES algorithm is not supported by the current JVM
	 * @throws NoSuchPaddingException
	 *             if the padding method is not supported by the current JVM
	 */
	public void init(
		final LSLAESCryptoMode mode,
		final LSLAESCryptoPad pad,
		final int padSize,
		final String hexKey,
		final String hexIV)
		throws NoSuchAlgorithmException,
			NoSuchPaddingException {
		if ((mode == null) || (pad == null) || (hexKey == null) ||
			(hexIV == null))
			throw new IllegalArgumentException("No arguments may be null");

		this.setMode(mode);
		this.setPad(pad, padSize);
		this.setKey(hexKey);
		this.setInputVector(hexIV);

		this.random.nextInt();

		this.createCipher();
	}

	/**
	 * Sets the input-vector for this engine to use
	 * 
	 * @param hexIV
	 *            a hexadecimal input-vector to use
	 */
	public void setInputVector(final String hexIV) {
		if (hexIV == null)
			throw new IllegalArgumentException("Input-vector may not be null!");

		this.ivSpec = new IvParameterSpec(HexCoder.hexToBytes(hexIV));
	}

	/**
	 * Sets the key for this engine to use
	 * 
	 * @param hexKey
	 *            a hexadecimal key to use
	 */
	public void setKey(final String hexKey) {
		if (hexKey == null)
			throw new IllegalArgumentException("Key may not be null!");

		this.keySpec = new SecretKeySpec(HexCoder.hexToBytes(hexKey), "AES");
	}

	/**
	 * Sets the mode of this implementation
	 * 
	 * @param mode
	 *            the mode to set
	 */
	public void setMode(final LSLAESCryptoMode mode) {
		if (mode == null)
			throw new IllegalArgumentException("Mode may not be null!");

		this.mode = mode;
		this.modeChanged = true;
	}

	/**
	 * Sets the padding scheme of this implementation
	 * 
	 * @param pad
	 *            the padding scheme to use
	 */
	public void setPad(final LSLAESCryptoPad pad) {
		this.setPad(pad, this.padSize);
	}

	/**
	 * Sets the padding scheme of this implementation
	 * 
	 * @param pad
	 *            the padding scheme to use
	 * @param padSize
	 *            the block-size to use when padding. Must be a non-zero,
	 *            positive value that is a multiple of 128.
	 */
	public void setPad(final LSLAESCryptoPad pad, final int padSize) {
		if (pad == null)
			throw new IllegalArgumentException("Pad may not be null!");
		if ((padSize <= 0) || ((padSize % 128) > 0))
			throw new IllegalArgumentException(
				"Pad size may not be less than zero, and must be a multiple of 128");

		this.pad = pad;
		this.padSize = padSize;
	}

	/**
	 * Creates a new cipher instance for processing
	 * 
	 * @throws NoSuchPaddingException
	 *             if the padding scheme set is invalid
	 * @throws NoSuchAlgorithmException
	 *             if AES is not supported by this JVM
	 */
	protected void createCipher()
		throws NoSuchAlgorithmException,
			NoSuchPaddingException {
		this.cipher = Cipher.getInstance("AES/" + this.mode + "/NoPadding");
	}

	/** Defines modes of operation combatible with LSL */
	public enum LSLAESCryptoMode {
		/** Cipher-Block-Chaining mode */
		CBC,
		/** Cipher FeedBack mode */
		CFB;
	}

	/** Defines padding schemes compatible with LSL */
	public enum LSLAESCryptoPad {
		/** Performs no padding, will switch to RBT if mode is CBC. */
		NONE,
		/**
		 * Enables CFB mode temporarily for the final complete block, and
		 * combines with data. This preserves data-length.
		 */
		RBT,
		/**
		 * Adds null-bytes to the end of the data until it is of correct-size.
		 * This is an padding scheme (may result in loss of null-bytes from
		 * original data).
		 */
		NULLS,
		/**
		 * Same as NULLS, except that it first appends a single '1' bit to the
		 * data before padding.
		 */
		NULLS_SAFE,
		/**
		 * Appends null-bytes to the data until one word from block-size, final
		 * word is then populated with bytes describing the number of padding
		 * bytes added.
		 */
		ZEROES,
		/**
		 * Same as ZEROES, except that random-bytes are used in place of
		 * null-bytes.
		 */
		RANDOM;
	}
}

Examples

Encryption

import lslAESCrypto.LSLAESCrypto;
import lslAESCrypto.LSLAESCrypto.LSLAESCryptoMode;
import lslAESCrypto.LSLAESCrypto.LSLAESCryptoPad;

/** */
public class ExampleEncrypt {
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(final String[] args) throws Exception {
		final String myKey = "1234567890ABCDEF0123456789ABCDEF";
		final String myIV = "89ABCDEF0123456789ABCDEF01234567";
		final String myMsg = "Hello world! I am a lovely message waiting to be encrypted!";

		final LSLAESCrypto aes = new LSLAESCrypto(
			LSLAESCryptoMode.CFB,
			LSLAESCryptoPad.NoPadding,
			myKey,
			myIV);
		System.out.println(aes.encrypt(myMsg));
	}
}

Decryption

import lslAESCrypto.LSLAESCrypto;
import lslAESCrypto.LSLAESCrypto.LSLAESCryptoMode;
import lslAESCrypto.LSLAESCrypto.LSLAESCryptoPad;

/** */
public class ExampleDecrypt {
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(final String[] args) throws Exception {
		final String myKey = "1234567890ABCDEF0123456789ABCDEF";
		final String myIV = "89ABCDEF0123456789ABCDEF01234567";
		final String myMsg = "Mdn6jGTwRPMOKTYTTdDKGm9KScz26LIz96KVOGAeMw3hpwByPfa07PDRHxRW4TIh5dmu5LlhKpTQChi=";

		final LSLAESCrypto aes = new LSLAESCrypto(
			LSLAESCryptoMode.CFB,
			LSLAESCryptoPad.NoPadding,
			myKey,
			myIV);
		System.out.println(aes.decrypt(myMsg));
	}
}