AES Java Implementation: Difference between revisions
Jump to navigation
Jump to search
New page: = Description = The following is a simple Java example of AES encryption and decryption, compatible with the LSL AES Engine by [[User:Haravikk Mistral|Haravikk M... |
|||
| Line 19: | Line 19: | ||
public class Base64Coder { | public class Base64Coder { | ||
/** Mapping table from 6-bit nibbles to Base64 characters. */ | /** Mapping table from 6-bit nibbles to Base64 characters. */ | ||
private static char[] map1 = new char[ | private static char[] map1 = new 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-bit nibbles. */ | /** Mapping table from Base64 characters to 6-bit nibbles. */ | ||
private static byte[] map2 = new byte[ | private static byte[] map2 = new byte[] { -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, }; | |||
/** | /** | ||
Revision as of 09:15, 15 September 2008
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>
* This is "Open Source" software and released under the <a
* href="http://www.gnu.org/licenses/lgpl.html">GNU/LGPL</a> license.<br>
* It is provided "as is" without warranty of any kind.<br>
* Copyright 2003: Christian d'Heureuse, Inventec Informatik AG, Switzerland.<br>
* Home page: <a href="http://www.source-code.biz">www.source-code.biz</a>
*/
public class Base64Coder {
/** Mapping table from 6-bit nibbles to Base64 characters. */
private static char[] map1 = new 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-bit nibbles. */
private static byte[] map2 = new byte[] { -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) {
int iLen = in.length;
if (iLen % 4 != 0)
throw new IllegalArgumentException(
"Length of Base64 encoded input string is not a multiple of 4.");
while ((iLen > 0) && (in[iLen - 1] == '='))
iLen--;
final int oLen = (iLen * 3) / 4;
final byte[] out = new byte[oLen];
int ip = 0;
int op = 0;
while (ip < iLen) {
final int i0 = in[ip++];
final int i1 = in[ip++];
final int i2 = ip < iLen ? in[ip++] : 'A';
final int i3 = ip < iLen ? in[ip++] : 'A';
if ((i0 > 127) || (i1 > 127) || (i2 > 127) || (i3 > 127))
throw new IllegalArgumentException(
"Illegal character in Base64 encoded data.");
final int b0 = Base64Coder.map2[i0];
final int b1 = Base64Coder.map2[i1];
final int b2 = Base64Coder.map2[i2];
final int b3 = Base64Coder.map2[i3];
if ((b0 < 0) || (b1 < 0) || (b2 < 0) || (b3 < 0))
throw new IllegalArgumentException(
"Illegal character in Base64 encoded data.");
final int o0 = (b0 << 2) | (b1 >>> 4);
final int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2);
final int o2 = ((b2 & 3) << 6) | b3;
out[op++] = (byte) o0;
if (op < oLen) out[op++] = (byte) o1;
if (op < oLen) out[op++] = (byte) o2;
}
return out;
}
/**
* 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, 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 iLen
* number of bytes to process in <code>in</code>.
* @return A character array with the Base64 encoded data.
*/
public static char[] encode(final byte[] in, final int iLen) {
final int oDataLen = (iLen * 4 + 2) / 3; // output length without
// padding
final int oLen = ((iLen + 2) / 3) * 4; // output length including
// padding
final char[] out = new char[oLen];
int ip = 0;
int op = 0;
while (ip < iLen) {
final int i0 = in[ip++] & 0xff;
final int i1 = ip < iLen ? in[ip++] & 0xff : 0;
final int i2 = ip < iLen ? in[ip++] & 0xff : 0;
final int o0 = i0 >>> 2;
final int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
final int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
final int o3 = i2 & 0x3F;
out[op++] = Base64Coder.map1[o0];
out[op++] = Base64Coder.map1[o1];
out[op] = op < oDataLen ? Base64Coder.map1[o2] : '=';
op++;
out[op] = op < oDataLen ? Base64Coder.map1[o3] : '=';
op++;
}
return out;
}
/**
* 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() {}
}
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 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;
/** Out currently set padding mode */
protected LSLAESCryptoPad pad = LSLAESCryptoPad.NoPadding;
/** 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;
/**
* 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 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 String hexKey,
final String hexIV)
throws NoSuchAlgorithmException,
NoSuchPaddingException {
this.init(mode, pad, 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 {
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 {
this.cipher.init(Cipher.ENCRYPT_MODE, this.keySpec, this.ivSpec);
return new String(
Base64Coder.encode(this.cipher.doFinal(text.getBytes())));
}
/**
* 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 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 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);
this.setKey(hexKey);
this.setInputVector(hexIV);
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;
}
/**
* Sets the padding scheme of this implementation
*
* @param pad
* the padding scheme to use
*/
public void setPad(final LSLAESCryptoPad pad) {
if (pad == null)
throw new IllegalArgumentException("Pad may not be null!");
this.pad = pad;
}
/**
* 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 + "/" + this.pad);
}
/** 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 {
/** No padding. Doesn't work with CBC. */
NoPadding,
/** Equivalent to PAD_ZEROES <b>or</b> PAD_RANDOM in LSL */
ISO10126Padding;
}
}
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));
}
}