Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this simple AES encryption work?

Why does not this AES encryption work? I've written it in Java to test, but I am not able to decrypt. I get garbage upon decryption. Why? Its so simple - In the main method, print plain text, encrypt, print cipher text, decrypt, print plain text again. Am I doing something wrong? Please help me figure out the problem.



import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESTest {
    public static void main(String [] args) {
        try {
            String plainText = "Hello World!!!!!";
            String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";

            System.out.println("Before encryption - " + plainText);

            String cipherText = encrypt(plainText, encryptionKey);

            System.out.println("After encryption - " + cipherText);

            String decrypted = decrypt(cipherText, encryptionKey);

            System.out.println("After decryption - " + decrypted);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    public static String encrypt(String plainText, String passkey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
        SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        String cipherText = new String(cipher.doFinal(plainText.getBytes()));
        return cipherText;
    }

    public static String decrypt(String cipherText, String passkey) throws Exception{
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
        SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        String plainText = new String(cipher.doFinal(cipherText.getBytes()));
        return plainText;
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                 + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}
like image 942
sat Avatar asked Aug 18 '11 21:08

sat


People also ask

Is AES 256 still secure in 2021?

AES-256 is definitely secure for file storage. The only weakness is the key that you choose. As long as you choose a strong key for it, AES-256 will keep your files safe. According to this Wikipedia page, the best attack on AES was published in 2011 and to break AES-256, it still required 2^254.4 operations.

How do you do AES encryption?

Dividing data into blocks Each of its blocks contains a column of 16 bytes in a layout of four-by-four. As one byte contains 8 bits, we get 128-bit block size (16x8=128). Thus, the very first step of AES encryption is dividing the plaintext (text that is not written in code) into these blocks.

What is the best mode for AES encryption?

XTS mode is the most common if you are encoding a random accessible data (like a hard disk or RAM). OCB is by far the best mode, as it allows encryption and authentication in a single pass.

Can AES encryption be broken?

AES 256 is virtually impenetrable using brute-force methods. While a 56-bit DES key can be cracked in less than a day, AES would take billions of years to break using current computing technology. Hackers would be foolish to even attempt this type of attack. Nevertheless, no encryption system is entirely secure.


3 Answers

The output of the cipher is a sequence of random-looking bytes. You have no guarantee that these bytes will be a valid encoding for a character string in whatever is your system's default encoding. So this line:

 String cipherText = new String(cipher.doFinal(.....));

is likely to lose information that you'll need for decryption.

Therefore you will not get the right bytes reconstructed in your decrypt operation. For example, if your default encoding is UTF-8, it is overwhelmingly unlikely that the correct ciphertext is something that String.getBytes() is even able to produce.

like image 194
hmakholm left over Monica Avatar answered Sep 22 '22 09:09

hmakholm left over Monica


Two things:

No padding can only work if you use input that is an exact mulitple of your key size, which is 128 bit or 16 bytes. So in your particular case "Hello World!!!!!".getBytes() is actually a multiple of 16, but this is of course not true for arbitrary Strings.

Use "AES/CBC/PKCS5Padding" instead to solve this issue.

Do not turn your encrypted data into a String - this will and change the encrypted output. There's no guarantee that new String(byte[]).getBytes() returns the exact same byte array! So you should leave the encrypted data as what it is - a stream of bytes. Thus encrypt should return byte[] instead and decrypt should take byte[] as input - this is a working example:

public class NewClass {
    public static void main(String [] args) {
        try {
            String plainText = "Hello World!!!!";
            String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";

            System.out.println("Before encryption - " + plainText);

            byte[] cipherText = encrypt(plainText, encryptionKey);

            System.out.println("After encryption - " + cipherText);

            String decrypted = decrypt(cipherText, encryptionKey);

            // -> Hello World!!!!
            System.out.println("After decryption - " + decrypted);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    public static byte[] encrypt(String plainText, String passkey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
        SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        return cipher.doFinal(plainText.getBytes());
    }

    public static String decrypt(byte[] cipherText, String passkey) throws Exception{
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
        SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new    byte[cipher.getBlockSize()]));
        return new String(cipher.doFinal(cipherText));
} 
like image 26
emboss Avatar answered Sep 24 '22 09:09

emboss


You need to create the SecretKeySpec object once and use it for both encrypt and decrypt. Currently the code is creating two different keys for each operation and this will definitely lead to incorrect results.

like image 22
uperez Avatar answered Sep 21 '22 09:09

uperez