When I use C# to implement the AES symmetric encryption cipher, I noticed:
PasswordDeriveBytes derivedPassword = new PasswordDeriveBytes(password, saltBytesArray, hashAlgorithmName, numPasswordIterations);
Why do I need to use a hashing algorithm for AES encryption? Aren't they separate? Or is the hashing algorithm only used to create a secure key?
The AES algorithm doesn't use a hashing algorithm internally does it?
PasswordDeriveBytes isn't part of AES. It implements an algorithm to derive encryption keys from a password. The algorithm involves the usage of a hash algorithm.
PasswordDeriveBytes is used to derived a symmetric key as well as the IV you are going to use for encryption/decryption. The PasswordDeriveBytes would take an passkey (password in your case), append the salt (saltBytesArray in your case) and hash it (with the algorithm you provided, e.g. SHA or MD5) 'n' many times (numPasswordIterations in your case) and give you the resulatant byte array.
This method is only used to derive the key, this has got nothing to do with encryption decryption per say.
Related
I am using Rfc2898DeriveBytes to generate an AES key and iv. However, I heard that the iv should not be dependent on the password. Here's how I'm doing it right now:
byte[] salt = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(plainStrPassword, salt)) {
byte[] aesKey = rfc.GetBytes(32);
byte[] iv = rfc.GetBytes(16); // Should I do this or generate it randomly?
}
My question: Is it OK (secure) to generate the iv from Rfc2898DeriveBytes? Or should I generate it randomly using RNGCryptoServiceProvider?
Let's look at your code;
byte[] salt = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(plainStrPassword, salt)) {
byte[] aesKey = rfc.GetBytes(32);
byte[] iv = rfc.GetBytes(16); // Should I do this or generate it randomly?
}
Random salt - Good
Rfc2898DeriveBytes with salt; this is good as long as the user password has good strength. The strength (not entropy!) of the derived key cannot exceed the passwords' strength.
Call GetBytes(32) for Key - Good, this is what is expected.
Call GetBytes(16) for IV -
This is good, too; since
Repeated calls to this method will not generate the same key; instead, appending two calls of the GetBytes method with a cb parameter value of 20 is the equivalent of calling the GetBytes method once with a cb parameter value of 40.
For each encryption, you can continue to get a new IV by calling GetBytes(16). Of course, there is a limit to that. PKKDF2 standard limits the output 2^32-1 * hLen, see in RFC 8018.
There is nothing wrong with outputting some part as IV and keeping some part as the encryption key. There are already tons of password schemes using PBKDF2 and non have been broken even the password hash and salt has been known.
If you are fearing that is not a good idea, then you can use either;
Generate two salts and derive the IV and encryption key separately form password as;
byte[] saltForKey = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfcKey = new Rfc2898DeriveBytes(plainStrPassword, saltForKey)) {
byte[] aesKey = rfcKey.GetBytes(32);
byte[] saltForIV = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfcIV = new Rfc2898DeriveBytes(plainStrPassword, saltForIV)) {
byte[] iv = rfcIV.GetBytes(16); // Should I do this or generate it randomly?
}
Generate random Salt and derive the encryption key and jus generate a random IV
byte[] salt = GenerateRandomBytes(32); // Generates 32 random bytes for Salt
byte[] IV = GenerateRandomBytes(16); // Generates 16 random bytes of IV
using (Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(plainStrPassword, salt)) {
byte[] aesKey = rfc.GetBytes(32);
}
Note that, you did not define the encryption mode. For a mode like
CTR mode, the 96-bit nonce, and 32-bit counter are common. For this, the 96-bit nonce can be generated by a counter/LFSR, too. Make sure that a (key,IV) pair never occurs.
CBC mode, the nonce must be random and unpredictable. The above is fine for this.
Of course, you should forget those and use authenticated encryption modes like AES-GCM, ChaCha20-Poly1305. If you fear the IV reuse then use AES-GCM-SIV that can only leak that you sent the same message, nothing else leaked. SIV mode is just to times slower, since it must pass the plaintext to derive the IV, then encryption is executed.
No, it's not secure to derive the IV from the same source from which you derive the key. The IV exists so that encryption of identical messages under the same key produces different ciphertexts.
You should use a cryptographically secure random source (such as RNGCryptoServiceProvider you identified) to derive the IV and communicate it alongside the ciphertext (typically either prepended to the ciphertext as one stream of bytes or in a separate field within a more structured file format).
Many cryptographic algorithms are expressed as iterative algorithms. E.g., when encrypting a message with a block cipher in CBC mode, each message "block" is first XORed with the previous encrypted block, and the result of the XOR is then encrypted. The first block has no "previous block" hence we must supply a conventional alternate "zero-th block" which we call "initialization vector". Generally speaking, an IV is whatever piece of data is needed to begin running an algorithm, and is not secret (if it was secret, we would call it a "key", not an IV).
IV is an arbitrary constant so any value will work. Make sure your encryptor and decryptor uses the same value. For more info you can refer these links:
https://crypto.stackexchange.com/questions/732/why-use-an-initialization-vector-iv
https://crypto.stackexchange.com/questions/3965/what-is-the-main-difference-between-a-key-an-iv-and-a-nonce
Based on this MS Docs, it is fine to use Rfc2898DeriveBytes for generating iv from password. Rfc2898DeriveBytes is implementation of PBKDF2, which the purpose is for: password-based key derivation functionality. See the example there.
PS: you should use RNGCryptoServiceProvider for generating salt.
RijndaelManaged is an algorithm while AES is the standard. When referring to AES256, does that mean the key length I'm using with RijndaelManaged must be 256 characters?
If I have a key like this:
key = "mytestkey";
which is only 9 characters, does that mean I'm using AES9?
Aes/Rijndael key sizes are in bits, not characters; AES256 uses a 256-bit key. You must give it a key that is exactly 256 bits. The AES standard and Rijndael only accept key sizes that are either 128, 192, or 256 bits.
You should not interchange AES and Rijndael. The AES standard is derived from Rijndael, but they are not exactly the same. If you wish to use AES, use AES for all operations; if you wish to use Rijndael, use Rijndael for all operations.
"mytestkey" cannot directly be a key to AES256 nor a similar Rijndael mode; it is not 256 bits long. In order to use it as a key, you'll have to transform it into a block of bytes that is 256 bits long.
If you're working with passwords, one typical means to do this is with key stretching, using hash algorithms such as PBKDF2 or Scrypt. PBKDF stands for "Password-based key derivation function", which is basically exactly what you're doing - deriving a key from a password.
Theoretically you could also just hash the password with SHA256 (which always has a 256-bit output) and use that 256-bit block as the key to AES; doing so is unwise from a security standpoint because it is relatively easy to precompute SHA hashes of passwords.
Please keep in mind that if you use a password that has very little entropy, then the security of your encryption suffers - the time it'll take for someone to guess the key could be short. "mytestkey" has at most ~42 bits of entropy - you're only using lower case letters, so 26 values per place, and there are 9 places (9 characters). Thus the number of bits theoretically needed to stores this is log_2( 26^9 ) = 42.3. In this circumstance, you'd be using AES256 with a key that has only ~42 bits of entropy.
I should note that the advice given here is an incomplete treatment of how to turn passwords into keys, from a security perspective. If you want to better understand how to properly generate keys from passwords, I suggest you start with some reading such as the Password Storage Cheat Sheet at owasp.org.
I'm writing an encryption sequence for sensitive data in our database.
Currently I'm taking a GUID based on the UserId, and putting that through a hash. Then, I run the hash through a Rfc2898DeriveBytes to get Key and IV which I use to encrypt the data using the Rijndael function.
My code looks like this:
var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
const int iterations = 1000;
using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(GenerateHash("2525"), salt, iterations)) {
_key = rfc2898DeriveBytes.GetBytes(32);
_iv = rfc2898DeriveBytes.GetBytes(16);
}
I then pass the _key and _iv along to decrypt or encrypt the data.
My goal is to have each user always have access to their unique key through every session. That being said, what can be randomized and still maintain this function? Do I always have to use the same salt and the same IV to get the data I want?
Rfc2898DeriveBytes is an implementation of PBKDF2. Obviously RFC 2898 is a reference to the standard where this Password Based Key Derivation Function has been defined. Note that the standard is broader than just the KDF; it's full title is "PKCS #5: Password-Based Cryptography Specification, Version 2.0".
PBKDF2 is a successor of PKCS#5 v1 which defined PBKDF / PBKDF1. The 1 was only added after PBKDF2 came into being. The class PasswordDeriveBytes is an implementation of PBKDF1. It should not be used anymore because both the KDF is outdated but also because Microsoft screwed up the implementation severely; it may repeat output keying material if more than the output of the underlying hash - SHA-1 so 20 bytes - is requested.
Besides being used as KDF, PBKDF2 can also be used as password hashing function, where the hash instead of the password is stored in a database. That way passwords can be verified, while the password cannot easily be retrieved even if the hash data is retrieved by an adversary. This is described in the followup RFC 8018 which contains the 2.1 version of the protocol.
Internally, PBKDF2 is just a repetition of a hash function over the password and salt. The iteration count is the work factor; it specifies how much work you (and adversaries) have to do before one hash is calculated. The salt makes sure that rainbow table attacks are impossible, and that identical passwords (of different users) don't lead to the same hash.
Due to a design error which requires the full amount of work to be repeated if more than one hash output is required, it is not recommended to request more data from it than the output of the hash function. In that case it is better to use another method to expand the output keying material (bytes), e.g. HKDF-Expand.
Observations on the code in the question:
The GenerateHash method is spurious, Rfc2898DeriveBytes will do this for you;
You should use something less predictable than a UID to create a key; the data should not be directly available to an attacker as this would completely defeat the purpose of PBKDF2;
If you want to use the same set of UID + salt + iterations for multiple encryption operations, then you should generate a random IV and prepend it to the ciphertext, having a non-random IV completely defeats the purpose of the IV;
You can change the salt to get multiple keys, but you would have to go through the PBKDF2 function for each and every encryption.
Just a general hint, only use the resulting key to encrypt data specific keys created out of a secure random function. Then you don't even need to bother about an IV, and you may be able to "re-encrypt" by decrypting the data specific key, and encrypting that with a new key.
Imagine I have this key (base 64):
Jdn1jJsD5hFrip4jzHODyA==
If I want to encrypt a string using AES 128 bit and the above key, what facilities does the .NET framework provide for this case?
I've tried searching on MSDN, but haven't found anything useful I could use.
Any guidance?
Your key appears to be encoded in Base64.
byte[] binKey = System.Convert.FromBase64String(textKey);
will give you a 16 byte key. All encryption classes use byte[] keys.
Is it possible to "calculate" a valid password string when the Key and the IV (as strings) for a RijndaelManaged-Key are known?
No it should not be possible. It depends which key derivation function
you use, but usually password and salt are repeatably hashed with MD5 or SHA1 so virtually impossible.