Can someone please explain what this program is doing pointing out some of the major points? I'm looking at the code and I'm completely lost. I just need explanation on the encryption/decryption phases. I think it generates an AES 192 key at one point but I'm not 100% sure. I'm not sure what the byte/ivBytes are used for either.
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
public class RandomKey
{
public static void main(String[] args) throws Exception
{
byte[] input = new byte[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
byte[] ivBytes = new byte[] {
0x00, 0x00, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
//initializing a new initialization vector
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
//what does this actually do?
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
//what does this do?
KeyGenerator generator = KeyGenerator.getInstance("AES","BC");
//I assume this generates a key size of 192 bits
generator.init(192);
//does this generate a random key?
Key encryptKey = generator.generateKey();
System.out.println("input: " +toHex(input));
//encryption phase
cipher.init(Cipher.ENCRYPT_MODE, encryptKey, ivSpec);
//what is this doing?
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
//what is this doing?
int ctLength = cipher.update(input, 0, input.length, cipherText,0);
//getting the cipher text length i assume?
ctLength += cipher.doFinal (cipherText, ctLength );
System.out.println ("Cipher: " +toHex(cipherText) + " bytes: " + ctLength);
//decryption phase
cipher.init(Cipher.DECRYPT_MODE, encryptKey, ivSpec);
//storing the ciphertext in plaintext i'm assuming?
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
//getting plaintextLength i think?
ptLength= cipher.doFinal (plainText, ptLength);
System.out.println("plain: " + toHex(plainText, ptLength));
}
private static String digits = "0123456789abcdef";
public static String toHex(byte[] data, int length)
{
StringBuffer buf = new StringBuffer();
for (int i=0; i!= length; i++)
{
int v = data[i] & 0xff;
buf.append(digits.charAt(v >>4));
buf.append(digits.charAt(v & 0xf));
}
return buf.toString();
}
public static String toHex(byte[] data)
{
return toHex(data, data.length);
}
}
//what does this actually do?
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
This uses the Bouncy Castle ("BC") provider to get an instance of the AES cypher, set up in Counter mode (CTR) with no padding (NoPadding). See Wikipedia for Block Cypher modes and Padding. The Javadoc will also help you.
//what does this do?
KeyGenerator generator = KeyGenerator.getInstance("AES","BC");
Again this uses the Bouncy Castle provider to set up a key generator for AES. You can read the Javadoc to learn more.
//what is this doing?
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
It sets up an array of bytes large enough to hold the encrypted output.
//what is this doing?
int ctLength = cipher.update(input, 0, input.length, cipherText,0);
It is actually doing the encyphering. Check the Javadoc for the update() method for a good explanation.
//getting the cipher text length i assume?
ctLength += cipher.doFinal (cipherText, ctLength );
No. Look at that += It is updating the cyphertext length. Again read the Javadoc for the differences between the update() and doFinal() methods.
Did you try looking here?
http://docs.oracle.com/javase/6/docs/api/javax/crypto/Cipher.html
http://docs.oracle.com/javase/6/docs/api/javax/crypto/KeyGenerator.html
The code you have is a pretty straight forward code example of how to encrypt and decrypt byte data in Java.
Once you have read through the class documentation you can better articulate your questions about the behavior of this code.
Related
i am facing weak key error while doing tripleDES encryption.Code working fine in java but giving error in C# .net.
i have java code in which TripleDES ecryption is working fine i need to convert my java code in c#.i am facing Weak key error during conversion.Below both java and c# code given.
1) Java Code
public class TripleDES {
private DESedeKeySpec desKeySpec;
public TripleDES(String key) {
try {
byte[] keyBytes = { (byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02,
(byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x02};
this.desKeySpec = new DESedeKeySpec(keyBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] encrypt(byte[] origData) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(this.desKeySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(origData);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public byte[] decrypt(byte[] crypted) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(this.desKeySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); //DESede/CBC/PKCS5Padding
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(crypted);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
TripleDES des = new TripleDES("");
byte[] data = { (byte)0x04, (byte)0x12, (byte)0x05, (byte)0xFF, (byte)0xFB, (byte)0xA6, (byte)0x66, (byte)0xCF};
//byte[] data = { (byte)0x04, (byte)0x12, (byte)0x15, (byte)0xAF, (byte)0xFD, (byte)0xD8, (byte)0x88, (byte)0xBB};
//-----------------Edited-----------------
String text = new BigInteger(1, data).toString(16);
System.out.println("Before encryption = " +text);
byte[] crypted = des.encrypt(data);
String text1 = new BigInteger(1, crypted).toString(16);
System.out.println("Encrypted = " +text1);
byte[] decrypted = des.decrypt(crypted);
String text2 = new BigInteger(1, decrypted).toString(16);
System.out.println("Decrypted = " +text2);
}
}
2) C# Code
static void Main(string[] args)
{
String Data = EncryptDES("041205FFFBA666CF");
}
public static string EncryptDES(string InputText)
{
byte[] key = new byte[] { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
byte[] clearData = System.Text.Encoding.UTF8.GetBytes(InputText);
MemoryStream ms = new MemoryStream();
TripleDES alg = TripleDES.Create();
alg.Key = key;
alg.Mode = CipherMode.ECB;
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.FlushFinalBlock();
byte[] CipherBytes = ms.ToArray();
ms.Close();
cs.Close();
string EncryptedData = Convert.ToBase64String(CipherBytes);
return EncryptedData;
}
Key: 02020202020202020202020202020202
Data : 041205FFFBA666CF
Result : A334C92CEC163D9F
Can anyone please write code in c# which produce result same as java.
Before I start, I want to say that I do not recommend or endorse you to follow this way for forcing TripleDES to use a key it thinks is weak, however, given that you are using it for Decryption only, here is a way to do it using Reflection.
Firstly, you must 'force' the TripeDES class to take the weak key you want to use. To do this, we use Reflection to bypass the check for a weak key that is performed when you attempt to set the key (alg.Key = key) and set the member variable directly:
//alg.Key = key; - THIS IS REPLACED BY THE BELOW
FieldInfo keyField = alg.GetType().GetField("KeyValue", BindingFlags.NonPublic | BindingFlags.Instance);
keyField.SetValue(alg, key);
The key value of TripleDES will now be set to your weak key (alg.Key);
Next you have a minor mistake, in that you have forgotten to turn off padding:
alg.Mode = CipherMode.ECB;
alg.Padding = PaddingMode.None; // Add this, as the default padding is PKCS7
Finally, there will be a further check for a weak key when creating the Decryptor, so we must use Reflection again to bypass the check and create the ICryptoTransform:
// Comment out the below line and use the code below
// CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
ICryptoTransform Decryptor;
MethodInfo createMethod = alg.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
Decryptor = createMethod.Invoke(alg, new object[] { alg.Key, alg.Mode, alg.IV, alg.FeedbackSize, 1 }) as ICryptoTransform;
CryptoStream cs = new CryptoStream(ms, Decryptor, CryptoStreamMode.Write);
The code will now run and accept weak keys, and perform the decryption you are looking for.
However, it does not look to me that the output is what you are expecting:
?Data
"4aU3DcHkiCTEywpiewWIow=="
At least now though you are able to use whichever key you want with TripleDES.
A 3DES key is a 24-byte value which is broken down two three 8-byte values: key0, key1, key2.
Because 3DES is DES_Encrypt(key2, DES_Decrypt(key1, DES_Encrypt(key0, data))) any time key1 is equal to key2 or key0 the algorithm is reduced to DES. This is what .NET's TripleDES is warning you about.
The proper test here should account for clearing (or setting, or fixing) the parity bits in each byte, but a hand-waved version is:
SymmetricAlgorithm alg;
IEnumerable<byte> key0 = key.Take(8);
IEnumerable<byte> key1 = key.Skip(8).Take(8);
IEnumerable<byte> key2 = key.Skip(16);
if (key0.SequenceEquals(key1))
{
alg = DES.Create();
alg.Key = key2.ToArray();
}
else if (key1.SequenceEquals(key2))
{
alg = DES.Create();
alg.Key = key0.ToArray();
}
else
{
alg = TripleDES.Create();
alg.Key = key;
}
To replace your two liner:
TripleDES alg = TripleDES.Create();
alg.Key = key;
Today following a guide I tried to encrypt and after decrypt a string.
Unluckily, I've gotten an InvalidKeyException, I googled up that and found out that for encrypt a string that have more than 128 bits (16 bytes -> 16 characters)
I need the JCE, so I got into the oracle website, downloaded it and putted it on my java build path, under the folder /lib/security.
Still no luck, i still keep getting this error, with this code:
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class EncryptTests {
public static void main(String[] args) throws Exception {
byte[] input = "www.java2s.com".getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
System.out.println(new String(input));
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
System.out.println(new String(cipherText));
System.out.println(ctLength);
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
System.out.println(new String(plainText));
System.out.println(ptLength);
}
}
This is the console output:
www.java2s.com Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default
parameters at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1026)
at javax.crypto.Cipher.implInit(Cipher.java:801) at
javax.crypto.Cipher.chooseProvider(Cipher.java:864) at
javax.crypto.Cipher.init(Cipher.java:1249) at
javax.crypto.Cipher.init(Cipher.java:1186) at
EncryptTests.main(EncryptTests.java:23)
Any fix? :)
I wrote the following code for text file encryption and decryption.
The encryption process works fine but I cannot decrypt the created file. For some reason, even though I use the same password, with the same length and same paddings, the decryption output is an encoded file different from the original one...
In other attempts I failed while running encryption and decryption in separated runs. But now they are both running one after another (even though they are using different key-spec, is that the reason? if so - how can I bypass it?)
public static void main(String[] args) throws Exception {
encrypt("samplePassword", new FileInputStream("file.txt"), new FileOutputStream("enc-file.txt"));
decrypt("samplePassword", new FileInputStream("enc-file.txt"), new FileOutputStream("file-from-enc.txt"));
}
public static void encrypt(String password, InputStream is, OutputStream os) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(password(password), "TripleDES");
Cipher cipher = Cipher.getInstance("TripleDES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] buf = new byte[8096];
os = new CipherOutputStream(os, cipher);
int numRead = 0;
while ((numRead = is.read(buf)) >= 0) {
os.write(buf, 0, numRead);
}
os.close();
}
public static void decrypt(String password, InputStream is, OutputStream os) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(password(password), "TripleDES");
Cipher cipher = Cipher.getInstance("TripleDES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] buf = new byte[8096];
CipherInputStream cis = new CipherInputStream(is, cipher);
int numRead = 0;
while ((numRead = cis.read(buf)) >= 0) {
os.write(buf, 0, numRead);
}
cis.close();
is.close();
os.close();
}
private static byte[] password(String password) {
byte[] baseBytes = { (byte) 0x38, (byte) 0x5C, (byte) 0x8, (byte) 0x4C, (byte) 0x75, (byte) 0x77, (byte) 0x5B, (byte) 0x43,
(byte) 0x1C, (byte) 0x1B, (byte) 0x38, (byte) 0x6A, (byte) 0x5, (byte) 0x0E, (byte) 0x47, (byte) 0x3F, (byte) 0x31,
(byte) 0xF, (byte) 0xC, (byte) 0x76, (byte) 0x53, (byte) 0x67, (byte) 0x32, (byte) 0x42 };
byte[] bytes = password.getBytes();
int i = bytes.length;
bytes = Arrays.copyOf(bytes, 24);
if(i < 24){
for(;i<24; i++){
bytes[i] = baseBytes[i];
}
}
return bytes;
}
can any one give me a hint?
On a first look,
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
should probably be
cipher.init(Cipher.DECRYPT_MODE, keySpec);
in the decrypt method.
Have a look at the documentation.
I have the following code.
byte[] input = etInput.getText().toString().getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
String strLength = new String(cipherText,"US-ASCII");
byte[] byteCiphterText = strLength.getBytes("US-ASCII");
Log.e("Decrypt", Integer.toString(byteCiphterText.length));
etOutput.setText(new String(cipherText,"US-ASCII"));
cipherText = etOutput.getText().toString().getBytes("US-ASCII");
Log.e("Decrypt", Integer.toString(cipherText.length));
ptLength += cipher.doFinal(plainText, ptLength);
Log.e("Decrypt", new String(plainText));
Log.e("Decrypt", Integer.toString(ptLength));
It works perfectly.
But once I convert it to the class. It always hit the error in this line.
ptLength += cipher.doFinal(plainText, ptLength);
Error:Pad block corrupted
I have checked and both code are exactly the same. Even the value passed in conversion string to byte all is no different from the code above. Any idea what's wrong with the code?
public String Encrypt(String strPlainText) throws Exception, NoSuchProviderException,
NoSuchPaddingException {
byte[] input = strPlainText.getBytes();
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return new String(cipherText, "US-ASCII");
}
public String Decrypt(String strCipherText) throws Exception,
NoSuchProviderException, NoSuchPaddingException {
byte[] cipherText = strCipherText.getBytes("US-ASCII");
int ctLength = cipherText.length;
byte[] keyBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// decryption pass
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText, 0);
ptLength += cipher.doFinal(plainText, ptLength);
return new String(plainText);
}
As Yann Ramin said, using String is a failure for cipher in/output. This is binary data that
can contain 0x00
can contain values that are not defined or mapped to strange places in the encoding used
Use plain byte[] as in your first example or go for hex encoding or base64 encoding the byte[].
// this is a quick example - dont use sun.misc inproduction
// - go for some open source implementation
String encryptedString = new sun.misc.BASE64Encoder.encodeBuffer(encryptedBytes);
This string can be safely transported and mapped back to bytes.
EDIT
Perhaps safest way to deal with the length issue is to always use streaming implementation (IMHO):
Example
static public byte[] decrypt(Cipher cipher, SecretKey key, byte[]... bytes)
throws GeneralSecurityException, IOException {
cipher.init(Cipher.DECRYPT_MODE, key);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (int i = 0; i < bytes.length; i++) {
bos.write(cipher.update(bytes[i]));
}
bos.write(cipher.doFinal());
return bos.toByteArray();
}
You have specified PKCS7 Padding. Is your padding preserved when stored in your String object? Is your string object a 1:1 match with the bytes output by the cipher? In general, String is inappropriate for passing binary data, such as a cipher output.
In your case cipher uses padding, that means in other words input data will be padded/rounded into blocks with some predefined size (which depends on padding algorithm). Let's say you have supplied 500 bytes to encrypt, padding block size is 16 bytes, so encrypted data will have size of 512 bytes (32 blocks) - 12 bytes will be padded.
In your code you're expecting encrypted array of the same size as input array, which causes exception. You need to recalculate output array size keeping in mind padding.
I'm trying to adapt this DES encrypting example to AES, so I made the changes, and it try to run this:
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
// Adapted from http://www.exampledepot.com/egs/javax.crypto/DesFile.html
public class AesEncrypter {
private Cipher ecipher;
private Cipher dcipher;
// Buffer used to transport the bytes from one stream to another
private byte[] buf = new byte[1024];
public AesEncrypter(SecretKey key) throws Exception {
// Create an 8-byte initialization vector
byte[] iv = new byte[] { (byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07, 0x72, 0x6F, 0x5A };
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
ecipher = Cipher.getInstance("AES/CBC/NoPadding");
dcipher = Cipher.getInstance("AES/CBC/NoPadding");
// CBC requires an initialization vector
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
}
public void encrypt(InputStream in, OutputStream out) throws Exception {
// Bytes written to out will be encrypted
out = new CipherOutputStream(out, ecipher);
// Read in the cleartext bytes and write to out to encrypt
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
}
public void decrypt(InputStream in, OutputStream out) throws Exception {
// Bytes read from in will be decrypted
in = new CipherInputStream(in, dcipher);
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
}
public static void main(String[] args) throws Exception {
System.out.println("Starting...");
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
InputStream in = new FileInputStream(new File("/home/wellington/Livros/O Alienista/speechgen0001.mp3/"));
OutputStream out = System.out;
AesEncrypter encrypter = new AesEncrypter(key);
encrypter.encrypt(in, out);
System.out.println("Done!");
}
}
but I got the Exception:
InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
So I tried to solve by changing the
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
for
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv, 0, 16);
but results in
IV buffer too short for given offset/length combination
I can just go trying till it works, but I would like to hear from who work with AES what is the commonly used buffer size?
Your main question has been answered, but I'd like to add that you should not in general be using a fixed string IV unless you know what you're doing. You maybe also want to use PKCS5Padding instead of NoPadding.
I think it's saying it wants 16 bytes, but this is only 8 bytes:
byte[] iv = new byte[] {
(byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07, 0x72, 0x6F, 0x5A
};
Maybe try this?
byte[] iv = new byte[] {
(byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07, 0x72, 0x6F, 0x5A,
(byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07, 0x72, 0x6F, 0x5A
};