1

I'm doing an encryption/decryption using AES. I'm trying to encrypt a text, then decrypt it separately later.

My AESEncrypt.java is:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.KeySpec;
import org.apache.commons.codec.binary.Base64;

public class AESEncrypt {
    private static final byte[] SALT = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03};
    private static final int ITERATION_COUNT = 65536;
    private static final int KEY_LENGTH = 256;
    private Cipher eCipher;
    private Cipher dCipher;

    AESEncrypt(String passPhrase) throws Exception {
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
        SecretKey secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
        SecretKey secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");

        eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        eCipher.init(Cipher.ENCRYPT_MODE, secretKey);

        dCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = eCipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
        dCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
    }

    public String encrypt(String encrypt) throws Exception {
        byte[] bytes = encrypt.getBytes("UTF8");
        byte[] encrypted = encrypt(bytes);
        return new String(Base64.encodeBase64(encrypted));
    }

    public byte[] encrypt(byte[] plain) throws Exception {
        return eCipher.doFinal(plain);
    }

    public static void main(String[] args) throws Exception {
        String passphrase = "PASSWORDPASSPHRASE";
        String password = "password123";    
        AESEncrypt aesEncrypt = new AESEncrypt(passphrase); 
        String encryptedPassword = aesEncrypt.encrypt(password);
        System.out.println("encryptedPassword = " + encryptedPassword);
    }
}

After running the AESEncrypt code above, the result is:

encryptedPassword = aUkbhjFebZ9VjJ44yptlBA==

So, now in my AESDecrypt.java, I'm using that encryptedPassword string to be decrypted:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.KeySpec;
import org.apache.commons.codec.binary.Base64;

public class AESDecrypt {
    private static final byte[] SALT = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03};
    private static final int ITERATION_COUNT = 65536;
    private static final int KEY_LENGTH = 256;
    private Cipher eCipher;
    private Cipher dCipher;

    AESDecrypt(String passPhrase) throws Exception {
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
        SecretKey secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
        SecretKey secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");

        eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        eCipher.init(Cipher.ENCRYPT_MODE, secretKey);

        dCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = eCipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
        dCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
    }

    public String decrypt(String encrypt) throws Exception {
        byte[] bytes = Base64.decodeBase64(encrypt);
        byte[] decrypted = decrypt(bytes);
        return new String(decrypted, "UTF8");
    }

    public byte[] decrypt(byte[] encrypt) throws Exception {
        return dCipher.doFinal(encrypt);
    }

    public static void main(String[] args) throws Exception {
        String passphrase = "PASSWORDPASSPHRASE";
        String encryptedPassword = "aUkbhjFebZ9VjJ44yptlBA==";        
        AESDecrypt aesDecrypt = new AESDecrypt(passphrase);
        String decryptedPassword = aesDecrypt.decrypt(encryptedPassword);
        System.out.println("decryptedPasswpord = " + decryptedPassword);
    }
}

When running the AESDecrypt code, an error is returned:

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 AESDecrypt.decrypt(AESDecrypt.java:41)
    at AESDecrypt.decrypt(AESDecrypt.java:35)
    at AESDecrypt.main(AESDecrypt.java:50)

Why do I get an error? Can anyone help and advise, please? Thank you very much!

1 Answer 1

5

When using CBC (Cipher Block Chaining) mode, you have to extract InitializationVector and append to your encrypted message, or find another way to pass the same IV into decryption code.

CBC mode is a more secure mode than ECB, which doesn't use an IV. CBC uses random IV which assures that the same data encrypted twice will not result in same ciphertext.

So your code should look like:

import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


public class AESEncrypt {
    private static final byte[] SALT = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03};
    private static final int ITERATION_COUNT = 65536;
    private static final int KEY_LENGTH = 128;
    private Cipher eCipher;
    private Cipher dCipher;
    private byte[] iv;

    AESEncrypt(String passPhrase) throws Exception {
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
        SecretKey secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
        SecretKey secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");

        eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        eCipher.init(Cipher.ENCRYPT_MODE, secretKey);

        dCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        iv = eCipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
        dCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
    }

    public String encrypt(String encrypt) throws Exception {
        byte[] bytes = encrypt.getBytes("UTF8");
        byte[] encrypted = encrypt(bytes);
        byte[] cipherText = new byte[encrypted.length + iv.length];
        System.arraycopy(iv, 0, cipherText, 0, iv.length);
        System.arraycopy(encrypted, 0, cipherText, iv.length, encrypted.length);
        return new String(Base64.encodeBase64(cipherText));
    }

    public byte[] encrypt(byte[] plain) throws Exception {
        return eCipher.doFinal(plain);
    }

    public static void main(String[] args) throws Exception {
        String passphrase = "PASSWORDPASSPHRASE";
        String password = "password123";    
        AESEncrypt aesEncrypt = new AESEncrypt(passphrase); 
        String encryptedPassword = aesEncrypt.encrypt(password);
        System.out.println("encryptedPassword = " + encryptedPassword);
    }
}

And:

 import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


public class AESDecrypt {
    private static final byte[] SALT = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03};
    private static final int ITERATION_COUNT = 65536;
    private static final int KEY_LENGTH = 128;
    private static final int IV_LENGTH = 16;
    private Cipher eCipher;
    private Cipher dCipher;
    private byte[] encrypt;

    AESDecrypt(String passPhrase, String encryptedString) throws Exception {
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
        SecretKey secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
        SecretKey secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");

        encrypt = Base64.decodeBase64(encryptedString);

        eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        eCipher.init(Cipher.ENCRYPT_MODE, secretKey);

        byte[] iv = extractIV();
        dCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        dCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
    }


    private byte[] extractIV() {
        byte[] iv = new byte[IV_LENGTH];
        System.arraycopy(encrypt, 0, iv, 0, iv.length);
        return iv;
    }

    public String decrypt() throws Exception {
        byte[] bytes = extractCipherText();

        byte[] decrypted = decrypt(bytes);
        return new String(decrypted, "UTF8");
    }

    private byte[] extractCipherText() {
        byte[] ciphertext = new byte[encrypt.length - IV_LENGTH];
        System.arraycopy(encrypt, 16, ciphertext, 0, ciphertext.length);
        return ciphertext;
    }

    public byte[] decrypt(byte[] encrypt) throws Exception {
        return dCipher.doFinal(encrypt);
    }

    public static void main(String[] args) throws Exception {
        String passphrase = "PASSWORDPASSPHRASE";
        String encryptedPassword = "LiN1KmaB2Pl7cooe3qvuImbAKXsVJt5oxt+ajuVZ5n4=";        
        AESDecrypt aesDecrypt = new AESDecrypt(passphrase, encryptedPassword);
        String decryptedPassword = aesDecrypt.decrypt();
        System.out.println("decryptedPasswpord = " + decryptedPassword);
    }
}
Sign up to request clarification or add additional context in comments.

18 Comments

In that case, is it possible to not use padding, but makes the program still secured? Any advice on how to do that? Thanks!
You have too :) Why is that a problem ? Padding size is constant so if u prepend or append it Base64 endoded to your crypto message you can transfer/store it freely. And strip it of on reception. Or you culd use ECB mode which may brake your security, because your data will be encrypted in parts and it might be posiible tu substitute them.
Can you give an example on how can I extract the padding and IV from my encryption code? And how do I use them later in the decryption code?I'm very new with Java, and I'm especially totally blank with encryption. Thanks a lot!
Thank you so much for the code revision. When I run the encryption code, it returns a longer string than previously: KUv9NVj8jaDU9cegO15d4MIEQbRxO+XgJcKKznBQB7I=. Then I use that new long encrypted string in the decryption code you gave. But when I run the decryption program, another error is returned saying: Exception in thread "main" java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long. Did I do something wrong here? Thanks!
CBC is not 'the most secure' mode. It's 'the most commonly used'. Please don't make such statements when you haven't enumerated all the possible modes of operation. See Wikipedia for a listing.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.