4

I'm developing an encryption utility class to be reused for common operations.

A very common case is to encrypt a plaintext with a user-provided password.
In this case, I'm using PBKDF2 to derive a valid AES key, then use it in GCM mode to encrypt the plaintext.

Some code:

// IV_LEN = 96
// ITERATIONS = 1000 ~ 4000
// KEY_LEN = 128 ~ 256
// TAG_LEN = 128
public static String encrypt(byte[] plain, char[] password) throws GeneralSecurityException
{
    SecureRandom rng = SecureRandom.getInstanceStrong();
    byte[] iv = new byte[IV_LEN / 8];
    rng.nextBytes(iv);

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
    SecretKey derivedKey = factory.generateSecret(new PBEKeySpec(password, iv, ITERATIONS, KEY_LEN));
    SecretKey secretKey = new SecretKeySpec(derivedKey.getEncoded(), "AES");

    Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
    c.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LEN, iv));

    byte[] encrypted = c.doFinal(plain);

    Encoder encoder = Base64.getUrlEncoder().withoutPadding();

    return encoder.encodeToString(iv) + ":" + encoder.encodeToString(encrypted);
}

Currently, I'm using the PBKDF2 salt (96 bit - SecureRandom) also as the IV for AES/GCM encryption.

Both the salt and IV can be public, but they shouldn't be reused.

Is it to be understood that they shouldn't be reused within the same Feature/Service/Algorithm, or that they shouldn't be reused anywhere?

It's very easy to modify this method to generate different salt and IV, but is there a reason to do it?

Thanks

Michele Mariotti
  • 7,372
  • 5
  • 41
  • 73
  • Is there any reason *not* to do it? – erickson Feb 09 '18 at 16:15
  • @erickson Actually, no - It's just morbid curiosity... and the returned record is some byte shorter! :) – Michele Mariotti Feb 09 '18 at 16:25
  • I would strongly recommend that you use a standard cryptographic message format, like CMS (PKCS #7), hopefully implemented by some well-reviewed and actively maintained library. If you do, it will carry parameters for both the content encryption cipher and the password-based key derivation. You won't save any bytes this way, but you are likely to have better security. – erickson Feb 09 '18 at 16:30
  • @erickson thanks for the suggestion. Nevertheless there's not much on Java CMS PBE around. I found only some basic examples using asymmetric keys with BouncyCastle. I'll deepen. – Michele Mariotti Feb 09 '18 at 17:04
  • 1
    @erickson The problem with CMS (PKCS #7), presumably [RFC 5084](https://tools.ietf.org/html/rfc5084), is that the RFC is less than user-friendly and uses [ASN.1](https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One). Do you have a better user oriented reference and examples? Without better information the uptake is going to be low. – zaph Feb 09 '18 at 17:15
  • @zaph: Agreed, PKCS#7 and its profiles (e.g. CMS) are disasters. I think *you* should write an RFC for a simple, useful, and secure message format and that doesn't try to be everything to everybody. – President James K. Polk Feb 09 '18 at 17:20
  • 1
    @James "I think you should write" Well, I do not have the experience or credentials to do that, *you* would be a much better candidate. – zaph Feb 09 '18 at 17:22
  • @zaph: Too late, I nominated you. :) – President James K. Polk Feb 09 '18 at 17:23
  • 1
    @JamesKPolk Perhaps something like the following probably pulling some concepts from RFC 5084 and the like: { algorithm:{ name: mode: key_size: block_size: } mode: { name initializer } padding: { name: } key_derivation: { name: key: count: } authentication: { hash_name: key_method: } additional: { user defined key/value fields } } data But that is unwieldy for encryption just a block or two. Now your turn. – zaph Feb 09 '18 at 18:03

1 Answers1

9

Note that you may not need to re-generate a random IV as long as you change the salt and therefore the resulting key. If you do not change the salt each time you (re-)encrypt then you do need a separate IV or you may leak information to an adversary.

You can keep the salt and IV the same. But it is generally easier to derive the IV together with the key from the password and salt. If you use PBKDF2 with a SHA-512 hash this is easy: just take 128, 192 or 256 bits of AES key from the resulting hash and then take another 128 subsequent bits as IV and use that.

If you need multiple keys or if you use a smaller hash then you may need to derive more keys from the result of PBKDF2. In that case it is best to mark the result of PBKDF2 as a master secret and perform N key derivations from it, one for each key and one for the IV. You could use e.g. HKDF for this, which Bouncy Castle has an implementation of (for which I provided the initial source code).

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • So it should be safe. Actually, this method uses a one-time salt/iv and only one derived aes-key to encrypt a small message. None of these are reused/reusable, since the salt/iv is random-generated and aes-key is derived from random-salt/iv. Thanks for the suggestions: I didn't thought I can use part of derived key as aes-key and part as IV, it's simple and smart. Also I'll keep in mind HKDF slave keys in multi-key situations. Thanks – Michele Mariotti Feb 10 '18 at 11:52