if I use a wrong key or wrong salt for decryption an BadPaddingException is thrown.
I would expect an incorrect string to be returned.
The doFinal() causes the exception in the decrypt-method
Message : This is just an example
Unfug : 'ΩÙΩ„SåF?V®ßs.k˚·ºç€èÀHfif∫ÙÉÕ
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at casino.AES.decryptString(AES.java:130)
at casino.AES.main(AES.java:172)
public static void main(String[] args) throws Exception {
//Encryption
AES encr = new AES();
encr.setKey("KEY");
encr.setSalt("SALT");
encr.setup();
String message = "This is just an example";
System.out.println("Message : " + message);
byte[] code = encr.encrypt(message);
System.out.println("Encrypted Strinng : "+ new String(code, "UTF-8"));
//Decryption
AES dec = new AES();
dec.setKey("INCORRECT"); //<--- incorrect
dec.setSalt("SALT");
dec.setup();
System.out.println(dec.decryptString(code));
}
public synchronized void setKey(String key) throws UnsupportedEncodingException {
this.key = key.getBytes("UTF-8");
isPasswordAlreadySet = true;
}
public synchronized void setSalt(String salt) throws UnsupportedEncodingException {
this.salt = salt.getBytes("UTF-8");
}
public synchronized void setup() throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key);
digest.update(salt);
byte[] raw = digest.digest();
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
}
public synchronized byte[] encrypt(byte[] klartext) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(klartext);
return encrypted;
}
public synchronized byte[] encrypt(String klartext) throws Exception{
return encrypt(klartext.getBytes("UTF-8"));
}
public synchronized byte[] decrypt(byte[] code) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original = cipher.doFinal(code);
return original;
}
public synchronized double decryptDouble(byte[] code) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original = cipher.doFinal(code);
return doubleFromBytes( original);
}
Thank you!
Frederik
The padding is a fine sanity check. Assuming the incorrectly decrypted data is uniformly distributed, it will only appear to be correctly PKCS5/PKCS7 padded about 1 time for every 255 incorrect passwords. (1/256 + 1/256^2 + 1/256^3 ...)
So it's helpful, but it's not something you should rely on --- what is effectively an almost-8-bit message digest is not a sufficient test of data integrity.
One more thing: If an attacker can repeatedly alter the ciphertext and get you to decrypt it, (an example might be encrypted data stored in a cookie) and if they can distinguish between your behavior when the decrypted data throws an exception for bad padding and when it is simply garbage, then they can determine the plaintext via a "padding oracle attack".
By the way, if you actually want the behavior you expected, you can use "AES/CTR/NoPadding", which will not require an exact block size and will always return a decrypted byte[], whether or not the keys match.
You should either use AES with an implicit padding declaration (see the available modes) or force the length (in bytes) of data encrypted/decrypted to be a multiple of 16.
Also, by default, java uses ECB mode, which can be really unsecured depending on which type of data you are using, you should probably use a CBC mode.
SecretKeySpec specifies a secret key in a provider-independent fashion
http://themasterofmagik.wordpress.com/2014/03/19/simple-aes-encryption-and-decryption-in-java/
Related
I have the following code to encrypt-decrypt a string using a key and random IV. However during decrypt I get a lot of zeros at the end in my IDE.
public class Example {
private static final String AES_MODE = "AES/CBC/PKCS5Padding";
private static final String CHARSET = "UTF-8";
private static final String HASH_ALGORITHM = "SHA-256";
private static final String KEY = "SUPER_SECURE_KEY";
private static SecretKeySpec getSecretKey() throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] bytes = KEY.getBytes(CHARSET);
digest.update(bytes, 0, bytes.length);
byte[] key = digest.digest();
return new SecretKeySpec(key, "AES");
}
public static String encrypt(String message) {
if(message == null || message.isEmpty()) {
return "";
}
try {
final SecretKeySpec key = getSecretKey();
byte[] cipherText = encrypt(key, message.getBytes(CHARSET));
return Base64.getEncoder().encodeToString(cipherText);
} catch (Exception e) {
System.out.print(e.toString());
return "";
}
}
private static byte[] encrypt(final SecretKeySpec key, final byte[] message) throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = new byte[iv.length + cipher.getOutputSize(message.length)];
System.arraycopy(iv, 0, ciphertext, 0, iv.length);
cipher.doFinal(message, 0, message.length, ciphertext, iv.length);
return ciphertext;
}
// ========================================================================================
public static String decrypt(String base64EncodedCipherText) {
if(base64EncodedCipherText == null || base64EncodedCipherText.isEmpty()) {
return "";
}
try {
final SecretKeySpec key = getSecretKey();
byte[] decodedCipherText = Base64.getDecoder().decode(base64EncodedCipherText);
byte[] decryptedBytes = decrypt(key, decodedCipherText);
return new String(decryptedBytes, CHARSET);
} catch (Exception e) {
System.out.print(e.toString());
return "";
}
}
private static byte[] decrypt(final SecretKeySpec key, final byte[] decodedCipherText) throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
IvParameterSpec ivSpec = new IvParameterSpec(decodedCipherText, 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
int plainTextLength = decodedCipherText.length - cipher.getBlockSize();
byte[] plaintext = new byte[plainTextLength];
cipher.doFinal(decodedCipherText, cipher.getBlockSize(), plainTextLength, plaintext, 0);
return plaintext;
// return cipher.doFinal(decodedCipherText);
}
// ========================================================================================
public static void main(String[] args) {
String message = "Message to encrypt.";
String encryptedText = encrypt(message);
System.out.println(encryptedText);
String decryptedText= decrypt(encryptedText);
System.out.println(decryptedText);
}
}
The output I get in IntelliJ IDEA is:
here
I think I am correctly separating the IV from the ciphertext, and decrypt the ciphertext with the key and the random IV. But still end up getting zeros in the end. Any pointers to what is wrong?
Reading is fundamental. The docs for getOutputSize indicate you can't use it for this purpose:
The actual output length of the next update or doFinal call may be smaller than the length returned by this method.
Encrypt it then check the resulting byte array, or do something with the return value of the doFinal method (which really tells you how many bytes it made), or make a ByteArrayOutputStream and send both the iv and the bytes from doFinal (taking into account what it returns) there, then ask it for the byte[], or use a ByteBuffer.
Note that CBC is dubious, as is pass hashing with SHA-256. It works, but it's 'too fast', it's very easy for a hacker to try a few billion passwords a second. In general you shouldn't be handrolling this stuff.
CBC mode as normally used requires padding, which your code correctly specifies, so the ciphertext (before adding and after removing the IV) is longer than the plaintext. You allocate a buffer for this longer size and Cipher.doFinal only stores the actual plaintext to it, leaving the remaining bytes with the value initialized by new byte[n] which is (always) zero.
You could determine the size the output will be using ciper.getOutputSize(int) much as you did for encrypt This doesn't work; Maarten is right.
You could continue to overallocate the output buffer, but save the return value from cipher.doFinal (input,off,len, output,off) which is an int that tells you the number of bytes output (decrypted), and then use only that many bytes from the buffer e.g. new String (output, 0, outlen, charset) or Arrays.copyOf(output, outlen)
But the easiest way is to use the doFinal overload that allocates the buffer itself (with the correct size) and returns it:
return cipher.doFinal(decodedCipherText, cipher.getBlockSize(), decodedCipherText.length - cipher.getBlockSize());
Concur with not using a simple hash on a password, but your example doesn't show or say if your 'key' is really a password (handled by humans, and needing 'stretching') or just a text form of something with adequate entropy, for which a simple hash is okay.
public static String decrypt(byte[] text, PrivateKey key) {
byte[] decryptedText = null;
try {
final Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedText = cipher.doFinal(text);
} catch (Exception e) {
e.printStackTrace();
}
return new String(decryptedText);
}
There is my code, for some reason I get this error and I think it is something to do with using the default constructor of the Cipher class.
Why am I getting this error?
In order to lead to reasonable results, you often don't want to encrypt an arbitrary number of bytes.
Instead: you enlarge the number of bytes to encrypt using padding.
See here on the "why to pad?"
3des with 2 different keys in java getting null.
import java.security.spec.*;
import javax.crypto.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
System.out.println("firstEncryption Value : "+firstEncryption);
String decryption = desDecryption(firstEncryption, k2);
System.out.println("decryption Value : "+decryption);
String secondEncryption = desEncryption(decryption, k1);
System.out.println("secondEncryption Value : "+secondEncryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
}
keyGen.init(112); // key length 56
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] encryptedText = cipher.doFinal(strToEncrypt.getBytes());
String encryptedString =base64encoder.encode(encryptedText);
return encryptedString;
} catch (Exception ex) {
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(strToDecrypt);
byte[] plainText = cipher.doFinal(encryptedText);
String decryptedString= bytes2String(plainText);
return decryptedString;
} catch (Exception ex) {
}
return null;
}
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i <bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
}
while i'm running the above code i'm getting null values. plz help.
output:
firstEncryption Value : jAihaGgiOzBSFwBWo3gpbw==
decryption Value : null
secondEncryption Value : null
getting error:
firstEncryption Value : ygGPwCllarWvSH8td55j/w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:
294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:145)
at DESedeEncryption.main(DESedeEncryption.java:107)
decryption Value : null
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:130)
at DESedeEncryption.main(DESedeEncryption.java:109)
secondEncryption Value : null
Symmetric ciphers work by encrypting and decrypting with the same key, hence the name symmetric. (And for most modes also the same IV, but the IV doesn't need to be secret.) You're encrypting with one key and decrypting with an independent key which is different with overwhelming probability (i.e. it might the same once a zillion quillion eternities). That won't work.
Perhaps you are confused by the description of Triple-DES also known as 3DES DESede or TDEA. The original DES (or DEA) cipher uses a 56-bit key (in 8 bytes) which was secure in the 1960s but not now. Triple-DES was defined using DES as a building block but with a bundle of 3 keys (k1,k2,k3) which can also be treated as a combined 168-bit key (in 24 bytes); if k3=k1 the key is described as 112-bits although it is still stored as 24 bytes. Your call to KeyGenerator "DESede" .init(112) does exactly that; it generates a 24-byte bundle with k3=k1 and k2 different. For convenience in the past Triple-DES is defined to use single-DES to encrypt with k1, decrypt with k2, and encrypt with k3, and the reverse when decrypting, hence the name DES-EDE or DESede. See http://en.wikipedia.org/wiki/Triple_DES .
If you really want, you can implement Triple-DES yourself in Java using Cipher "DES" by doing E,D,E (or reverse D,E,D if used) and then wrapping the mode around that, see Java Triple DES encryption with 2 different keys . But it's much easier to just use Cipher "DESede", which it does the lot for you, treating DESede like any other symmetric cipher primitive, as answered in that question.
Also, mode ECB is dangerous. It is an exaggeration to say it is always insecure as some people do, but historically very many applications using it designed by non-experts are insecure. Unless you know much more than is evident in your question, or are following (or interfacing to) a design by someone who does, use a better established mode like CBC or CTR.
I have the following class I use to store encrypted preferences to use with my application (using interface with 3rd part site which does not support OAuth)...
public class CryptoTranslator {
private static SecretKey SEC_KEY;
/**
* #return the sEC_KEY
*/
public static SecretKey getSEC_KEY() {
return SEC_KEY;
}
public static String getSEC_KEY_String(){
return Base64.encodeToString(SEC_KEY.getEncoded(), Base64.DEFAULT);
}
/**
* #param sEC_KEY the sEC_KEY to set
*/
public static void setSEC_KEY(SecretKey sEC_KEY) {
SEC_KEY = sEC_KEY;
}
public static void setSEC_KEY_STRING(String sEC_KEY){
byte[] key = Base64.decode(sEC_KEY, Base64.DEFAULT);
SEC_KEY = new SecretKeySpec(key, 0, key.length, "AES");
}
public static void generateKey() throws NoSuchAlgorithmException {
// Generate a 256-bit key
final int outputKeyLength = 256;
SecureRandom secureRandom = new SecureRandom();
// Do *not* seed secureRandom! Automatically seeded from system entropy.
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(outputKeyLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
SEC_KEY = key;
}
private static byte[] getRawKey() throws Exception {
if (SEC_KEY == null){
generateKey();
}
byte[] raw = SEC_KEY.getEncoded();
return raw;
}
/**
*
*
* #param clear clear text string
* #param mode this should either be Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
* #return
* #throws Exception
*/
private static String translate(String clear, int mode) throws Exception {
if(mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
throw new IllegalArgumentException("Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(mode, skeySpec);
byte[] encrypted = cipher.doFinal(clear.getBytes());
return new String(encrypted);
}
public static String encrypt(String clear) throws Exception {
return translate(clear,Cipher.ENCRYPT_MODE);
}
public static String decrypt(String encrypted) throws Exception {
return translate(encrypted,Cipher.DECRYPT_MODE);
}
}
So now I have encrypted and stored the data. Now I want to pull it out...
String secString = settings.getString(SEC_KEY, null);
if (secString == null) {
try {
CryptoTranslator.generateKey();
settings.edit()
.putString(SEC_KEY,
CryptoTranslator.getSEC_KEY_String()).commit();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
CryptoTranslator.setSEC_KEY_STRING(secString);
}
try {
getUserNamePassword();
} catch (Exception ex) {
Log.i("Preferences",
"There was an issue getting username and password");
isStored = CRED_STATUS_DEF;
}
...
private static void getUserNamePassword() throws Exception {
isStored = settings.getBoolean(CRED_STATUS, CRED_STATUS_DEF);
if (isStored) {
if (settings.contains(USERNAME_KEY))
username = settings.getString(USERNAME_KEY, "");
if (settings.contains(PASSWORD_KEY))
password = settings.getString(PASSWORD_KEY, "");
}
isUsernamePasswordValid();
if (isStored) {
String username2 = CryptoTranslator.decrypt(username);
Log.d("Security", "Username encrypted");
String password2 = CryptoTranslator.decrypt(password);
username = username2;
password = password2;
Log.d("Security", "Password encrypted");
}
}
But this gives me the following error....
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
Can someone see what I am doing wrong?
Update
Ok per the response I went ahead and changed my code to the following...
public static final int IV_LENGTH = 16;
private static final String RANDOM_ALGORITHM = "SHA1PRNG";
...
private static String translate(String clear, int mode) throws Exception {
if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
throw new IllegalArgumentException(
"Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(generateIv());
cipher.init(mode, skeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(clear.getBytes());
return new String(encrypted);
}
...
private static byte[] generateIv() throws NoSuchAlgorithmException,
NoSuchProviderException {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] iv = new byte[IV_LENGTH];
random.nextBytes(iv);
return iv;
}
Now I get...
javax.crypto.BadPaddingException: pad block corrupted
To try and use hex changed to...
private static byte[] translate(byte[] val, int mode) throws Exception {
if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
throw new IllegalArgumentException(
"Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(generateIv());
cipher.init(mode, skeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(val);
return encrypted;
}
This seems to almost work (I am getting the .com back) but the chars are still pretty jumbled.
public static String encrypt(String clear) throws Exception {
byte[] test = translate(clear.getBytes(), Cipher.ENCRYPT_MODE);
return new String(Hex.encodeHex(test));
}
public static String decrypt(String encrypted) throws Exception {
return new String(translate(Hex.decodeHex(encrypted.toCharArray()), Cipher.DECRYPT_MODE));
}
*The converting to Hex and back is screwed up here.
So there are a couple of issues with your code.
First is the output of an AES cipher is not character data, you are mangling your ciphertext by trying to put it in a String. When you try to decrypt your mangled ciphertext it is now the wrong length. You need to Base64 or Hex encode the ciphertext if you want to store it in a String and then decode it back in to a byte[] before decrypting it.
Second, when you specify just AES for your cipher spec Java expands that to AES/ECB/PKCS5Padding. ECB is an insecure cipher mode if you intend to encrypt more than 1 block of data (16 bytes for AES). I recommend you switch to a different spec AES/CBC/PKCS5Padding should be acceptable. Using a mode other than ECB will require an Initialization Vector (IV). The IV should be randomly generated but does not need to be secret, so you can store as plaintext with your ciphertext as you'll need it to decrypt as well. The initialization vector needs to be one block in length (16 bytes for AES). Do not reuse the same IV with the same AES key ever, generate a new IV for each encryption being done.
Finally, if your going to store IV + ciphertext in a third party service I recommend you add a MAC (such as HMACSHA1). A MAC will ensure the integrity of your IV + ciphertext before you attempt to decrypt it. A MAC will require a secret key as well, and you should not use the same key you generated for the cipher itself. You can prepend the generated MAC to your IV + ciphertext, so now you are storing MAC + IV + ciphertext.
Android AES client side + PHP AES server side it will throw this error :)
The solution is:
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Please search over the internet for the full source code. I am under NDA and to lazzy to make anonymous my whole code regarding this part, but I am sure you will find it.
I want a secure solution for caching a user's password on their PC during their session.
I have trawled numerous AES examples and know that this has been answered elsewhere but I must say it is a little confusing. My aesSecretKey or aesInitialisationVector are not working in the decryption correctly but not sure where the issue lies.
Decrypting results in a javax.crypto.BadPaddingException: Given final block not properly padded exception.
My class looks like this
public class LockManagerTest {
// Need to share the IV and key between encode and decode
private static byte[] aesInitialisationVector;
private static SecretKey aesSecretKey;
private static Cipher aesCipher;
public LockManagerTest(String sessionKey) {
try {
byte[] key = getSecretKey(sessionKey.toCharArray(), getSalt(32),
65536, 128);
aesSecretKey = new SecretKeySpec(key, "AES");
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
AlgorithmParameters params = aesCipher.getParameters();
aesInitialisationVector =
params.getParameterSpec(IvParameterSpec.class).getIV();
} catch (Exception e) {
Util.handleException(e);
}
}
private static byte[] getSecretKey(char[] plaintext,
byte[] salt,
int iterations,
int keySize)
throws Exception {
PBEKeySpec spec = new PBEKeySpec(plaintext, salt, iterations, keySize);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
}
private static byte[] getSalt(int keyLength) throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] salt = new byte[keyLength];
random.nextBytes(salt);
return salt;
}
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
public byte[] decryptAes(byte[] ciphertext) throws Exception {
aesCipher.init(Cipher.DECRYPT_MODE,
aesSecretKey, new IvParameterSpec(aesInitialisationVector));
byte[] plaintext = aesCipher.doFinal(ciphertext);
return plaintext;
}
}
Comments regarding the level of security appreciated also.
You need to pass the IV when calling init() in encryptedAes().
AES is a CBC algorithm and divides input into blocks. These blocks must be of a specific size. In the case of AES, I believe it is 16 bytes. If the input is not a multiple of 16 bytes, it must be padded with nulls before encryption.
Instead of generating new IV while decrypting, you need to pass same IV which you use for encrypting. Remember AES is Symmetric Cipher.
Edit:
What you are doing is:
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
Instead store the IvParameterSpec as a static, as per below (u can do proper variable declaration in your program)
public byte[] encryptedAes(char[] input) throws Exception {
//declare as static so initVector can be reused when decrypting
IvParamterSpec initVector = new IvParameterSpec(aesSecretKey);
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey, initVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
make the changes and then run your program. make sure you use the same initVector while decrypting. in your program you are creating new IvParameterSpec(...)