Exception in AES decryption algorithm in java - java

This is the code for encrypting and decrypting a string in java using AES algorithm. Its throwing illegalblocksize exception while decrypting.
I know it is occuring because length of input string to the decryption method isn't matching with the padding. I don't have an idea of how to solve this.
I am a new bie to the encryption decryption. Plz help me....
StackTrace:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
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 test.AES.AESdecryptalgo(AES.java:76)
at test.AES.main(AES.java:95)
Code:
package test;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
Cipher cipher;
SecretKeySpec skeySpec;
IvParameterSpec spec;
byte [] iv;
/*public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}*/
public AES()
{
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
catch(Exception ex)
{ex.printStackTrace();}
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
try
{
// byte[] raw = skey.getEncoded();
//SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
AlgorithmParameters param = cipher.getParameters();
IvParameterSpec ivspec=param.getParameterSpec(IvParameterSpec.class);
iv=ivspec.getIV();
spec=new IvParameterSpec(iv);
//AlgorithmParameters params = cipher.getParameters();
//iv = params.getParameterSpec(IvParameterSpec.class).getIV();
encrypted = cipher.doFinal(text);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
newtext=new String(encrypted);
//System.out.println("ENCRYPTED "+newtext);
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
try
{
cipher.init(Cipher.DECRYPT_MODE, skeySpec,spec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
String encrypt=a.AESencryptalgo("hello".getBytes());
System.out.println(encrypt);
String decrypt=a.AESdecryptalgo(encrypt.getBytes());
System.out.println(decrypt);
}
}`

You have to provide an initialization vector when using CBC mode.
When encrypting, let the provider choose the IV for you:
…
cipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
…
Later, when decrypting, use the same IV to initialize the cipher:
…
IvParameterSpec spec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
…

You must change AESencryptalgo to return byte[] rather than a String. This is where the trouble begins:
newtext = new String(encrypted);
// System.out.println("ENCRYPTED "+newtext);
return newtext;
After changing the return type of the method, you should make the following change:
//newtext = new String(encrypted);
// System.out.println("ENCRYPTED "+newtext);
//return newtext;
return encrypted;
The problem is that a String is a sequence of characters, whereas the encrypted text is a sequence of bytes (for a good summary of this difference, see The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)).
When you try to construct a String from a byte array, Java tries its best to convert those bytes into characters, using the system's default character set. Unfortunately, this mapping doesn't always work successfully (typically when the encrypted bytes fall outside the ASCII character set). You will only ever notice the problem when it comes time to decrypt your new String (which will fail to properly convert the sequence of characters back into the original sequence of bytes).

Related

How to decrypt a text?

I am developing an application, where I am encrypting and decrypting a text entered by the user.
But, I am getting the following error:
javax.crypto.IllegalBlockSizeException: last block incomplete in
decryption
below is my code for encryption and decryption. Encryption works perfectly, while I am getting this error while decrypting. Please refer the code below:
public static String fncEncrypt(String strClearText, String strKey) throws Exception{
String strData = "";
try {
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(strClearText.getBytes());
strData = new String(encrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
public static String fncDecrypt(String strEecrypted, String strKey) throws Exception {
String strData = "";
try {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(strEecrypted.getBytes());
strData = new String(decrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
Please respond if you have a solution for this.
You should decode the string instead of encoding the platform specific representation of the string, right at the start of your method.
byte[] base64TextToDecrypt = Base64.decodeBase64(textToDecrypt);
or more precisely:
byte[] bytesToDecrypt = Base64(base64TextToDecrypt);
if you name your variables correctly.
In general, each time you (feel like you have to) use the String.getBytes(): byte[] method or the String(byte[]) constructor you are likely doing something wrong. You should first think about what you are trying to do, and specify a character-encoding if you do need to use it.
In your case, the output in the converted variable is probably character-encoded. So you you could use the following fragment:
String plainText = new String(converted, Charset.forName("UTF8"));
System.out.println(plainText);
instead of what you have now.
Reference : https://stackoverflow.com/a/13274072/8416317
String class method getBytes() or new String(byte bytes[]) encode / decode with Charset.defaultCharset().name(), and some encrypted data would be ignored by encoding with special charset.
you could directly return byte[] by fncEncrypt and input byte[] to fncDecrypt. or encode result with BASE64.
public static byte[] fncEncrypt(String strClearText, String strKey) throws Exception{
byte[] encrypted = null;
try {
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
encrypted = cipher.doFinal(strClearText.getBytes());
} catch (Exception e){
e.printStackTrace();
}
return encrypted;
}
public static String fncDecrypt(byte[] ecrypted, String strKey) throws Exception {
String strData = "";
try {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(ecrypted);
strData = new String(decrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
The reason is when you use new String(encrypted) it will not fully encode the bytes to string. Try the code below
public static byte[] fncEncrypt(String strClearText, String strKey) throws Exception{
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(strClearText.getBytes());
return encrypted;
}
public static String fncDecrypt(byte[] encrypted, String strKey) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted);
}
You can encrypt and decrypt using the code below
String message = "Hello!";
byte[] encrypted = fncEncrypt(message, "key");
String decrypted = fncDecrypt(encrypted, "key");
System.out.println(decrypted);

How to create a AES Cipher using 128 bit string contains 128 0's

I am using the below code to create a 128 bit AES cipher,
String initializer = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
byte[] initializer2 = new BigInteger(initializer, 2).toByteArray();
String encrypt = encrypt(binary_string_firsthalf, initializer2);
public static String encrypt(String plainText, byte[] key) {
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF8"));
String encryptedString = new String(Base64.encode(cipherText));
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
But the code is returning a exception saying invalid AES key length.
How to create a cipher using 128 0's.
byte[] initializer2 = new byte[16];
is all you need to initialize a byte array with the default value of 0x00.
Don't use a static predictable key such as all 0x00 bytes for actual encryption.

how to convert a string in to a byte array

I've wrote a method to encrypt/decrypt a string. Encryption is happening successfully but I cannot manage to make the decryption work... This is the code I have written:
public String encrypt(String a, int x) {
String ret = "";
String text = a;
String key = "Bar12345Bar12345"; // 128 bit key
try {
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
if(x == 0) { //x==0 means I want to encrypt
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
ret =new String(encrypted);
}
else { //if not 0 I want to decrypt
// decrypt the text
byte[] encrypted = text.getBytes(Charset.forName("UTF-8"));
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted));
ret=decrypted;
}
} catch(Exception e) {
e.printStackTrace();
}
return ret;
}
I think the problem arise when I'm trying to convert the string into byte array. The error I get is:
javax.crypto.BadPaddingException: Given final block not properly padded
So what is the problem ? If I am not converting the string into byte array in right way, how should I do it ?

Cache user password using AES -- BadPaddingException

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(...)

Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher

In my application I am encrypting and decrypting data using secretKey. For that I am using AES algorithm. But I am getting exception in decrypt, one value out of three already encrypted values using secret key.
Exception is:
Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher.
Below is my code:
Function to encyrpt value
public static String symmetricEncrypt(String text, String secretKey) {
BASE64Decoder decoder = new BASE64Decoder();
byte[] raw;
String encryptedString;
SecretKeySpec skeySpec;
BASE64Encoder bASE64Encoder = new BASE64Encoder();
byte[] encryptText = text.getBytes();
Cipher cipher;
try {
raw = decoder.decodeBuffer(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
encryptedString = bASE64Encoder.encode(cipher.doFinal(encryptText));
}
catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
Function to decrypt value
public static String symmetricDecrypt(String text, String secretKey) {
BASE64Decoder decoder = new BASE64Decoder();
BASE64Decoder base64Decoder = new BASE64Decoder();
Cipher cipher;
String encryptedString;
byte[] encryptText = null;
byte[] raw;
SecretKeySpec skeySpec;
try {
raw = decoder.decodeBuffer(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
encryptText = base64Decoder.decodeBuffer(text);
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
encryptedString = new String(cipher.doFinal(encryptText));
} catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
Following are the values that I am encrypting and decrypting
String secretKey = "XMzDdG4D03CKm2IxIWQw7g==";
String value1= "ABCD";
String enctypedValue1= "3uweh4pzoVyH1uODQmVNJA==";
String enctypedValue2= "37PTC20w4DMZYjG3f+GWepSvAbEJUccMXwS/lXilLav1qM/PrCTdontw5/82OdC1zzyhDEsFVRGo rV6gXAQcm+Zai15hliiUQ8l8KRMtUl4=";
String value4= "20000";
/** Ecnryption and decryption of value1 **/
String encryptedValue1= symmetricEncrypt(value1, secretKey);
String decryptedValue1 = symmetricDecrypt(encryptedValue1, secretKey);
/** Decryption of enctypedValue1 **/
String decryptedValue2 = symmetricDecrypt(enctypedValue1, secretKey);
System.out.println(decryptedValue2);
/** Decryption of enctypedValue2 (Not decrypted)**/
String decryptedValue3 = symmetricDecrypt(enctypedValue2, secretKey);
System.out.println(decryptedValue3);
/** Ecnryption and decryption of value4 **/
String encryptedValue4= symmetricEncrypt(value4, secretKey);
String decryptedValue4 = symmetricDecrypt(encryptedValue4, secretKey);
In the test function, I have written the following three test cases.
A new value (value1) being encrypted and decrypted using a secret key.
Two example encrypted values (enctypedValue1, enctypedValue2) which are being decrypted using same secret key. encryptedValue2 which got a problem while decrypting using same secret key.
A new value (value4) being encrypted and decrypted using a secret key.
On decrypting encryptedValue2 I am getting the following exception:
Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher
Following is what I have derived till now.
The problematic value seems to have a problem while decoding it, it returns 81 length array which is unable to get decrypted?
If this problem was to happen it should have happened to all the values.
Is this a value specific problem or it is something related to padding or it can have a different behavior on different browser, different os?
I was able to run the code without any problem. However, I used Apache's Base64 for encoding/decoding...maybe your Base64 has bugs. If you wrote it yourself, there is a big chance that you missed some cases. For real production code, use heavily tested libraries such as Apache's.
You can find the library that I used for Base64 here: http://commons.apache.org/proper/commons-codec/download_codec.cgi
Here is the full working code:
package security.symmatric;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AES {
public static String symmetricEncrypt(String text, String secretKey) {
byte[] raw;
String encryptedString;
SecretKeySpec skeySpec;
byte[] encryptText = text.getBytes();
Cipher cipher;
try {
raw = Base64.decodeBase64(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
encryptedString = Base64.encodeBase64String(cipher.doFinal(encryptText));
}
catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
public static String symmetricDecrypt(String text, String secretKey) {
Cipher cipher;
String encryptedString;
byte[] encryptText = null;
byte[] raw;
SecretKeySpec skeySpec;
try {
raw = Base64.decodeBase64(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
encryptText = Base64.decodeBase64(text);
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
encryptedString = new String(cipher.doFinal(encryptText));
} catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
public static void main(String[] args) {
String secretKey = "XMzDdG4D03CKm2IxIWQw7g==";
String value1= "ABCD";
String enctypedValue1= "3uweh4pzoVyH1uODQmVNJA==";
String enctypedValue2= "37PTC20w4DMZYjG3f+GWepSvAbEJUccMXwS/lXilLav1qM/PrCTdontw5/82OdC1zzyhDEsFVRGo rV6gXAQcm+Zai15hliiUQ8l8KRMtUl4=";
String value4= "20000";
/** Ecnryption and decryption of value1 **/
String encryptedValue1= symmetricEncrypt(value1, secretKey);
String decryptedValue1 = symmetricDecrypt(encryptedValue1, secretKey);
System.out.println(decryptedValue1);
/** Decryption of enctypedValue1 **/
String decryptedValue2 = symmetricDecrypt(enctypedValue1, secretKey);
System.out.println(decryptedValue2);
/** Decryption of enctypedValue2 **/
String decryptedValue3 = symmetricDecrypt(enctypedValue2, secretKey);
System.out.println(decryptedValue3);
/** Ecnryption and decryption of value4 **/
String encryptedValue4= symmetricEncrypt(value4, secretKey);
String decryptedValue4 = symmetricDecrypt(encryptedValue4, secretKey);
System.out.println(decryptedValue4);
}
}

Categories