Mainly I'm trying to understand how to correctly create the Key and IV for use with the .NET Implementation of AES (AesManaged class).

This encryption code will be used in conjunction with existing customer records in a database.

Here is the process that I've come up with. First, get the key from an encrypted web.config and get a byte array using the Rfc2898DeriveBytes class. The salt is a constant.

public static byte[] GetKey(string key)
    var deriveBytes = new Rfc2898DeriveBytes(key, _salt);

    return deriveBytes.GetBytes(32);

Next, generate another byte array for the IV given the User's ID and the same salt from above. This method is essentially the same, but returns an array with 16 bytes instead of 32 (and the input is the User ID, not the key).

public static byte[] GetKey(string text)
    var deriveBytes = new Rfc2898DeriveBytes(text, _salt);

    return deriveBytes.GetBytes(16);

Given the Key and the IV I use a CryptoStream to encrypt the data:

var aesAlg = new AesManaged();

// Create an encryptor to perform the stream transform.
var encryptor = aesAlg.CreateEncryptor(key, iv);

// ... Code that uses the Crypto Stream

Main question(s):

  • Is there a better/different class that I should be using instead of Rfc2898DeriveBytes? It seems funny that the class name references an RFC.
  • Is the use of a constant salt bad? I'm doing this so that I don't need to store any additional information on the User record. Other examples I've seen use a constant IV, but a random Salt. Are those 2 scenarios essentially the same?

Cryptographically speaking, AesManaged uses AES in CBC mode. To ensure this operates securely, you need to choose the IV randomly, i.e. it should not be possible to predict the IV between iterations. This question has a discussion of non-random IVs: Using a Non-Random IV with modes other than CBC and this SO question: Why is using a Non-Random IV with CBC Mode a vulnerability? has a discussion about the chosen plaintext vulnerability using non-random IVs.

According to the documentation, Rfc2898DeriveBytes implements PBKDF2, a key stretching function. DeriveBytes is an interesting choice of name, but that's essentially what it'll do - output some bytes from the PBKDF2 algorithm. At its heart, PBKDF2 is a repeated application of SHA1; if you input the same passphrase and IV, you'll end up with the same encryption key.

Unlike a hash, you're not going to be storing this anywhere, as it is your secret key for your symmetric operation, so there's less risk of an attacker trying to find a preimage (recover the passphrase) mostly because they just don't need to. So the uniqueness requirement looks unnecessary - however, hypothetically speaking let us assume for a minute all your users use the same passphrase "I love asking crypto questions on SE". If they do this and you're using a constant salt, they'll have the same key. Non-repeating IVs for our CBC mode will mitigate the problem slightly, but it would be a lot better if the keys weren't all the same in the first place.

So, summary:

  • Constant Salt: bad. Pick a large salt, unique per user. Randomly picking salts will help you achieve this - if you generate a 64-bit salt that gives you a total possible output space of $2^{64} = 18446744073709551616$, or approximately $2635249153$ unique salts per human being right now. So you are not likely to run out, and guarantee that all your user's keys are unique assuming no collisions in PBKDF2.

    Edit thanks to fgrieu - the salt size must be 8-bytes (64-bits) or larger. If you can accommodate larger salts, you should, to avoid birthday attacks. For more information, see the comments.

  • Constant IV: bad. You definitely want a random IV for CBC mode.

  • $\begingroup$ Nitpick: if one picks a 64-bit salt at random for each human being, odds are high that two get the same salt. $\endgroup$ – fgrieu Jan 31 '12 at 12:39
  • 2
    $\begingroup$ In the context, collision of 64-bit salt it is not much of a problem: an attacker trying to find the password by brute force has a moderate advantage (by a factor of 2) for these these few humans which share the same salt. The simplest fix is a bigger salt, which is possible: according to Rfc2898DeriveBytes doc The salt size must be 8 bytes or larger. $\endgroup$ – fgrieu Jan 31 '12 at 13:23
  • 1
    $\begingroup$ @JohnBubriski Not quite. The IV needs to not predictable between messages. You can (and indeed have to, to recover the first block) send the IV with the message, so there isn't a requirement to store it, just that it needs to be unpredictable. The salt you'll need to store somewhere for key generation. $\endgroup$ – user46 Feb 1 '12 at 13:24
  • 1
    $\begingroup$ @JohnBubriski Ah, I'm thinking about a system where you have a message to send somewhere. In this case yes, since the "message" is in fact an entry in a database, you'd need to store them both. Now suppose you were going to store a list of holidays someone has made, beginning with a date. These are different messages and need different IVs (unless you encrypt the whole list in one go) so that an attacker cannot, by persuading you to insert dates they choose, derive any information about the plaintext or key. $\endgroup$ – user46 Feb 1 '12 at 13:49
  • 1
    $\begingroup$ Rfc2898DeriveBytes generates your key byte array and itself takes a salt - for generating the salt you'd need a CSPRNG - like this: stackoverflow.com/questions/1668353/…. You can use the same CSPRNG to generate your IV bytes - they simply need to be random - we might have a question already about deriving IV and keys from the output of PBKDF2 but that's stretching my personal expertise a bit. $\endgroup$ – user46 Feb 1 '12 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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