I'm using Rijndael to encrypt some sensitive data in my program.
When the user enters an incorrect password, most of the time a CryptographicException
is thrown with the message "Padding is invalid and cannot be removed.".
However, with very small probability, the CryptStream does not throw an exception with the wrong password, but instead gives back an incorrectly decrypted stream. In other words, it decrypts to garbage.
Any idea how to detect/prevent this? The simplest way I can think of would be to put a "magic number" at the start of the message when encrypting, and check if it's still there after decrypting.
But if there's an easier way, I'd love to hear it!
I like Can Gencer's answer; you cannot really verify a decryption without the HMAC.
But, if you have a very a very large plaintext, then the decrypting can be very expensive. You might do a ton of work just to find out that the password was invalid. It would be nice to be able to do a quick rejection of wrong passwords, without going through all that work. There is a way using the PKCS#5 PBKDF2. (standardized in RFC2898, which is accessible to your c# program in Rfc2898DeriveBytes).
Normally the data protocol calls for generation of the key from a password and salt using PBKDF2, at 1000 cycles or some specified number. Then maybe also (optionally) the initialization vector, via a contniuation of the same algorithm.
To implement the quick password check, generate two more bytes via the PBKDF2. If you don't generate and use an IV, then just generate 32 bytes and keep the last 2. Store or transmit this pair of bytes adjacent to your cryptotext. On the decrypting side, get the password, generate the key and (maybe throwaway) IV, then generate the 2 additional bytes, and check them against the stored data. If the pairs don't match you know you have a wrong password, without any decryption.
If they match, it is not a guarantee that the password is correct. You still need the HMAC of the full plaintext for that. But you can save yourself a ton of work, and maybe wall clock time, in most cases of "wrong password", and without compromising the security of the overall system.
ps: you wrote:
Avoid putting plaintext into the cryptotext. It only exposes another attack vector, makes it easier for an attacker to eliminate wrong turns. The password verification thing I mentioned above is a different animal, does not expose this risk.
To check if the password you are using is correct, you can use this code
in essence, check if an error message is generated during decryption.
I report all the decoding code below
Though I can agree somewhat with Teoman Soygul post about CRC/Hash there is one very important thing to note. Never encrypt the hash as this can make it easier to find the resulting key. Even without encrypting the hash you still gave them an easy way to test if they have successfully gained the correct password; however, let's assume that is already possible. Since I know what kind of data you encrypted, be it text, or serialized objects, or whatever, it's likely I can write code to recognize it.
That said, I've used derivations of the following code to encrypt/decrypt data:
HMAC is what you need. It is exactly made for this purpose. It combines the key and the message (which in this case, will be your password) and hashes them in a way that it will ensure the authenticity and integrity of the content, as long as the hash function used is secure. You can attach the HMAC to the encrypted data, and it can be used later to validate if the decryption was made correctly.
I use that code to decrypt any file. Wrong password detected on
cryptostream.close()
. Catch this line as error when a wrong key is used to decrypt file. When error happens, just close the output stream and release it (setoutputFile
toNothing
), then delete output file. It's working for me.Checksums are exactly for this purpose. Get a hash of your data before encrypting. Encrypt the data and put it along with the hash into storage. After decrypting, get the hash of the decrypted data and compare it with the former. If you use a crypto grade hash (i.e. SHA512) your data will be safe. After all, this is exactly what encrypted compression software does.
For ultimate security, you can encrypt both the hashes and data separately then decrypt and compare. If both data and hash decrypts to corrupted data, there is very minuscule chances that they will match.