Why Do I Need to Use the Rfc2898Derivebytes Class (In .Net) Instead of Directly Using the Password as a Key or Iv

Why do I need to use the Rfc2898DeriveBytes class (in .NET) instead of directly using the password as a key or IV?

You really, really do not want to use a user password directly as a crypto key, especially with AES.

Rfc2898DeriveBytes is an implementation of PBKDF2. What it does is repeatedly hash the user password along with the salt. This has multiple benefits:

Firstly, you can use arbitrarily sized passwords - AES only supports specific key sizes.

Secondly, the addition of the salt means that you can use the same passphrase to generate multiple different keys (assuming the salt is not a constant, as it is in your example). This is important for key separation; reusing keys in different contexts is one of the most common ways cryptographic systems are broken.

The multiple iterations (1000 by default) slow down password guessing attacks. Consider someone who is trying to guess your AES key. If you just used the password, this would be straightforward - just try each possible password as the key. On the other hand, with PBKDF2, the attacker first has to perform 1000 hash iterations for each password guess. So while it slows down a user only slightly, it has a disproportionate effect on an attacker. (In fact it's quite common to use much higher iteration counts; 10000 is commonly recommended).

It also means the final output key is uniformly distributed. If you used the password, for instance, typically 16 out of 128 bits of the key would be 0 (the high ASCII bit). That right there immediately makes keysearch 65536 times easier than it should be, even ignoring the password guessing.

Finally, AES has specific vulnerabilities with related key attacks. Related key attacks are possible when an attacker knows some data encrypted with several keys, and there is some known (or guessed) relation between them. For instance, if you encrypted data with both a password-key of "My AES key sucks" (16 bytes, for AES-128) and with "MY AES KEY SUCKS", a related key attack might be possible. The currently best known attacks do not actually allow breaking the full AES in this way, but they have been getting progressively better over time - just last week a new attack was published that breaks 13 rounds (out of 14 total) of AES-256 using a related key attack. It would be profoundly unwise to rely on such attacks not getting better over time.

How many bytes should a password hash be when using Rfc2898DeriveBytes?

This is related to the output length of _hashAlgorithmName you use. For example, if you use HMAC-SHA512, the output length is 512 bits (64 bytes) and getting more bytes than 64 bytes is a bit overkill. Even 256 bits of entrophy is enough for today's standards.

Actually, this is why ASP.NET Core Identity package uses only 32 bytes (256 bits) when hashing passwords with Rfc2898DeriveBytes by using HMAC-SHA512.

For reference:
https://github.com/dotnet/aspnetcore/blob/main/src/Identity/Extensions.Core/src/PasswordHasher.cs

Importance of salt when using Rfc2898DeriveBytes to create secure passwords from clear text passwords

Salts, in the context of password hashing (and key derivation), are used to prevent precomputation attacks like rainbow tables.

Note that the salt must be different and unpredictable (preferably random) for every password. Also note that salts need not be secret – that's what the password is for. You gain no security by keeping the salt secret.

The recommended approach in your case is to generate a random salt every time a file is encrypted, and transmit the salt along with the ciphertext.

Is there a specific reason you're using AES-256 by the way? It's around 40% slower than AES-128 due to the extra rounds, and it offers no practical security benefit (particularly not in the case of password-based encryption).

It's also worth considering using a well-established standard like PGP rather than building your own protocol from cryptographic primitives, because building secure protocols is so hard that even experts don't always get it right.

Rfc2898DeriveBytes + PBKDF2 + SecureString is it possible to use a secure string instead of a string?

After doing some research and looking at previous answers on stackoverflow mentioning SecureString, that answer is almost certainly: "No". Only the creators of the API can accept SecureString and handle it correctly internally. And they can only do that with help of the platform.

If you - as a user - could retrieve the plain text String, you would have negated most of the advantages of using SecureString in the first place. It would even be a bit dangerous as you would create secure looking code, that would not actually be secure at all (edit: at least not when it comes to protecting in-memory data).

Importance of the key size in the Rfc2898DeriveBytes (PBKDF2) implementation

Generally you use PKCS#5 v2 / RFC2898 to create a symmetric key from a user password. The size is important because it must match the required size of the symmetric algorithm you'll be using.

aes.Key = deriveBytes.GetBytes (16); // 16 * 8 = 128 bits

However you seems to be looking at keeping an hash of passwords, not for a key, so the size is not as important in your specific case. You can safely fix it to the hash size (20 bytes for SHA1) if you want a specific value.

General note (for people where performance matters): using PKCS#5 v2 (or older) will take a lot longer (iteration count) than using a salted hash or an HMAC.

Using Rfc2898DeriveBytes Class to obtain Key and IV

The RFC2898 key derivation algorithm is deterministic. When you run it with the same inputs, you'll get the same outputs.

So as long as you call GetBytes() in the same order, requesting the same number of bytes, you'll get the same key and IV.

Don't forget that in a real system the salt should be randomly generated at encryption time (and stored so that it can be retrieved at decryption time).



Related Topics



Leave a reply



Submit