Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need two keys for this AES encryption?

I'm trying to implement AES encryption for some text and I was looking for a solution that eventually comes down to one password for encryption and decryption. I've got a working solution that I found on this site: Simple Java AES encrypt/decrypt example

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

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

public class Encryptor {
    public static String encrypt(String key1, String key2, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(key2.getBytes("UTF-8"));

            SecretKeySpec skeySpec = new SecretKeySpec(key1.getBytes("UTF-8"),
                    "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(value.getBytes());
            System.out.println("encrypted string:"
                    + Base64.encodeBase64String(encrypted));
            return Base64.encodeBase64String(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String key1, String key2, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(key2.getBytes("UTF-8"));

            SecretKeySpec skeySpec = new SecretKeySpec(key1.getBytes("UTF-8"),
                    "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {

        String key1 = "Bar12345Bar12345"; // 128 bit key
        String key2 = "ThisIsASecretKet";
        System.out.println(decrypt(key1, key2,
                encrypt(key1, key2, "Hello World")));
    }
}

This algorithm is using key1 and key2 and don't understand why they need the two of them. What is the purpose of each key, and would it be safe to leave key1 as it is for all times and just replace key2 with a personal password?

Thanks

like image 811
user3116232 Avatar asked Aug 30 '25 16:08

user3116232


1 Answers

'key1' is your secret key. In this code, the byte array is obtained by calling String.getBytes() on a password. See this answer for why that isn't advisable, and some suggested alternatives.

'key2' is misnamed; it's actually an Initialization Vector (or 'IV' for short). This is a random sequence of bytes that prevents the same plaintext from always being transformed into the same ciphertext.

A random IV should be generated for each encrypted message using SecureRandom.getBytes(). For AES the IV should be 16 bytes (128 bits). The IV is not a secret; it should be sent along with the ciphertext so the recipient can use it (along with the secret key) to decrypt the message.

After encrypting, I would recommend prepending the IV bytes to the ciphertext bytes and then Base64-encoding the resulting byte array.

As for how the code is structured, there's no need to pass the IV into the encrypt method; the encrypt method can generate the IV internally. Likewise, if the ciphertext is prefixed with the IV then you don't need to pass the IV to the decrypt method; instead the decrypt method can assume the first 16 bytes of the message are the IV.

Generally speaking, encryption alone does not ensure the message hasn't been tampered with. If you want to detect when an attacker tampers with your encrypted messages, you can apply a Message Authentication Code like HMAC to the iv + cyphertext.

like image 183
dnault Avatar answered Sep 02 '25 06:09

dnault