AES Encryption IV's

23,731

An AES key simply consists of random bytes. For CBC mode the IV mode should also be randomized (at least to an attacker). So in general you can simply use a SecureRandom instance to create the key and IV. The IV can then be included with the ciphertext; usually it is simply put in front of it.

With Java it is better to use a KeyGenerator though. If you look at the implementation of it in the SUN provider it will probably amount to the same thing. However using a KeyGenerator is more compatible with various kinds of keys and providers. It may well be that it is a requirement for generating keys in e.g. smart cards and HSM's.

So lets show a class with three simple methods:

package nl.owlstead.stackoverflow;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Optional;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class CreateKeyAndIVForAES_CBC {

    public static SecretKey createKey(final String algorithm, final int keysize, final Optional<Provider> provider, final Optional<SecureRandom> rng) throws NoSuchAlgorithmException {
        final KeyGenerator keyGenerator;
        if (provider.isPresent()) {
            keyGenerator = KeyGenerator.getInstance(algorithm, provider.get());
        } else {
            keyGenerator = KeyGenerator.getInstance(algorithm);
        }

        if (rng.isPresent()) {
            keyGenerator.init(keysize, rng.get());
        } else {
            // not really needed for the Sun provider which handles null OK
            keyGenerator.init(keysize);
        }

        return keyGenerator.generateKey();
    }

    public static IvParameterSpec createIV(final int ivSizeBytes, final Optional<SecureRandom> rng) {
        final byte[] iv = new byte[ivSizeBytes];
        final SecureRandom theRNG = rng.orElse(new SecureRandom());
        theRNG.nextBytes(iv);
        return new IvParameterSpec(iv);
    }

    public static IvParameterSpec readIV(final int ivSizeBytes, final InputStream is) throws IOException {
        final byte[] iv = new byte[ivSizeBytes];
        int offset = 0;
        while (offset < ivSizeBytes) {
            final int read = is.read(iv, offset, ivSizeBytes - offset);
            if (read == -1) {
                throw new IOException("Too few bytes for IV in input stream");
            }
            offset += read;
        }
        return new IvParameterSpec(iv);
    }

    public static void main(String[] args) throws Exception {
        final SecureRandom rng = new SecureRandom();
        // you somehow need to distribute this key
        final SecretKey aesKey = createKey("AES", 128, Optional.empty(), Optional.of(rng));
        final byte[] plaintext = "owlstead".getBytes(UTF_8);

        final byte[] ciphertext;
        {
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();

            final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final IvParameterSpec ivForCBC = createIV(aesCBC.getBlockSize(), Optional.of(rng));
            aesCBC.init(Cipher.ENCRYPT_MODE, aesKey, ivForCBC);

            baos.write(ivForCBC.getIV());

            try (final CipherOutputStream cos = new CipherOutputStream(baos, aesCBC)) {
                cos.write(plaintext);
            }

            ciphertext = baos.toByteArray();
        }

        final byte[] decrypted;
        {
            final ByteArrayInputStream bais = new ByteArrayInputStream(ciphertext);

            final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final IvParameterSpec ivForCBC = readIV(aesCBC.getBlockSize(), bais);
            aesCBC.init(Cipher.DECRYPT_MODE, aesKey, ivForCBC);

            final byte[] buf = new byte[1_024];
            try (final CipherInputStream cis = new CipherInputStream(bais, aesCBC);
                    final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                int read;
                while ((read = cis.read(buf)) != -1) {
                    baos.write(buf, 0, read);
                }
                decrypted = baos.toByteArray();
            }
        }

        System.out.println(new String(decrypted, UTF_8));
    }
}

Note that you may not always want to generate and distribute an AES key "out-of-band". Here are a few other methods of generating a key (part #2 onwards). You may also want to take a look at more advanced exception handling for the cryptographic operation.

Share:
23,731
TravJav92
Author by

TravJav92

Aspiring developer who is passionate about code and technology.

Updated on July 09, 2022

Comments

  • TravJav92
    TravJav92 almost 2 years

    I am using this below (E.1) for my application, there is obviously a huge glaring security hole in this that I recognize and understand. I have grown interested in encryption and want to understand it better, I need to generate a random key along with an IV but am unsure how to do so properly Can someone explain to me whom is familiar with AES encryption how this works (IV & KEY) So I am better able to understand in the future and can apply my knowledge, essentially I just want to make the code more secure, thank you.

    (E.1)

        byte[] key = "mykey".getBytes("UTF-8");
    
        private byte[] getKeyBytes(final byte[] key) throws Exception {
            byte[] keyBytes = new byte[16];
            System.arraycopy(key, 0, keyBytes, 0, Math.min(key.length, keyBytes.length));
            return keyBytes;
        }
    
        public Cipher getCipherEncrypt(final byte[] key) throws Exception {
            byte[] keyBytes = getKeyBytes(key);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            return cipher;
        }
    
        public void encrypt(File in, File output, byte[] key) throws Exception {
    
            Cipher cipher = getCipherEncrypt(key);
            FileOutputStream fos = null;
            CipherOutputStream cos = null;
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(in);
                fos = new FileOutputStream(output);
                cos = new CipherOutputStream(fos, cipher);
                byte[] data = new byte[1024];
                int read = fis.read(data);
                while (read != -1) {
                    cos.write(data, 0, read);
                    read = fis.read(data);
                 System.out.println(new String(data, "UTF-8").trim());
    
                }
                cos.flush();
    
            } finally {
    
                System.out.println("performed encrypt method now closing streams:\n" + output.toString());
                cos.close();
                fos.close();
                fis.close();
    
    
            }
        }
    
    
    public void watchMeEncrypt(){
    
    encrypt(file, new File ("example.txt),key);
    
  • TravJav92
    TravJav92 about 8 years
    Hey thanks you for the example I will tale note and consider the architecture of it
  • TravJav92
    TravJav92 about 8 years
    Don't mean to double ask here but how would I put a secure random IV thats generated every single time because ion my code when i created it i used the keyBytes as the Iv which is bad.. Im just curious how to perfect the code I have without gutting it too much
  • Maarten Bodewes
    Maarten Bodewes about 8 years
    Just copy the relevant code from createIV. But note that in real life you need to somehow synchronize the IV between sender and receiver. The IV is specific to the ciphertext so you cannot just distribute it once as you do with the key.