5
\$\begingroup\$

I've done some reading about implementing AES256 and deriving a key from a password. If I understand correctly:

  • I want to generate a new salt (for the key) and a new IV (for the encrypted message) for every new message.
  • It should also not be a problem sending the salt and the IV together with the message.

I decided to pack everything together in one byte array where the first 16 bytes is the salt, the next 16 bytes is the IV and the rest is the encrypted message. This with a key length of 256 bits and 20000 iterations for generating the key. I also encode the whole thing in Base64 for transmission.

Can this approach be improved? Knowing that I'm limited to 256 bytes for the complete message (salt+iv+message).

public class app {

    public static void main(String[] args) throws Exception {
        int iterations = 20000;
        int keyLength = 32;
        byte[] salt = getRandomBytes(16);
        byte[] iv = getRandomBytes(16);
        char[] password = "password_here".toCharArray();
        byte[] payload = "payload_here".getBytes();

        byte[] key = deriveKey(iterations, keyLength, salt, iv, password);
        byte[] encrypted = encrypt(iv, key, payload);
        byte[] bytes = concatBytes(salt, iv, encrypted);

        String output = Base64.encodeBytes(bytes);
        int outputLength = output.getBytes().length;
        System.out.println(outputLength + "\n" + output);
    }

    private static byte[] deriveKey(int iterations, int length, byte[] salt, byte[] iv, char[] password)
            throws InvalidKeySpecException, NoSuchAlgorithmException {
        PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, length * 8);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        byte[] key = skf.generateSecret(spec).getEncoded();
        return key;
    }

    private static byte[] getRandomBytes(int length) throws NoSuchAlgorithmException {
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[length];
        sr.nextBytes(salt);
        return salt;
    }

    private static byte[] concatBytes(byte[]... arrays) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        for (byte[] array : arrays)
            outputStream.write(array);
        return outputStream.toByteArray();
    }

    private static byte[] encrypt(byte[] iv, byte[] key, byte[] text) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException,
            BadPaddingException {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        SecretKeySpec newKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
        return cipher.doFinal(text);
    }
}
\$\endgroup\$
6
  • \$\begingroup\$ Question - if you are limited to 256 bytes, why Base64 encode it? What is the protocol you are using on the 'internet', can it be a byte-stream? \$\endgroup\$
    – rolfl
    Commented Feb 5, 2014 at 21:50
  • \$\begingroup\$ Good question. It will be via HTTP (JSON). \$\endgroup\$
    – ndsc
    Commented Feb 5, 2014 at 21:55
  • \$\begingroup\$ You will likely get much better answers on Security.SE or Cryptography.SE, as this is kind of niche knowledge. \$\endgroup\$
    – asteri
    Commented Feb 5, 2014 at 22:09
  • 1
    \$\begingroup\$ @ndsc: Could you edit the question in a way that does not make existing answers meaningless, please? \$\endgroup\$
    – palacsint
    Commented Feb 17, 2014 at 22:17
  • 2
    \$\begingroup\$ You can post new code by appending to the end of the question or by asking a new question: see Can I edit my own question to include suggested changes from answers? \$\endgroup\$
    – ChrisW
    Commented Feb 18, 2014 at 0:10

1 Answer 1

5
\$\begingroup\$

A few, mostly minor notes:

  1. "payload_here".getBytes();
    

    getBytes() should have been called with a Charset. Otherwise it uses the platform's default charset which can vary from platform to platform and could result to data loss and/or other bugs.

  2. You have local variable called salt inside the getRandomBytes method. It's a little bit confusing since the same method is called for iv too:

    byte[] salt = new byte[length];
    
  3. The encrypt method could throw six different types of exceptions. It's a little bit too much. All of them is a subclass of GeneralSecurityException. I'd use that instead, I don't think that clients want to handle those six different types differently.

  4. According to the Code Conventions for the Java Programming Language class names should start with uppercase letters.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for your notes! I have since updated my code and I've edited it above. I agree with the exceptions. I have yet to write a good solution for that. \$\endgroup\$
    – ndsc
    Commented Feb 17, 2014 at 21:00

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.