views:

104

answers:

2

I'm using Rijndael to encrypt/decrypt some sensitive images that will be used on some documents. I'm trying to be absolutely sure that the password provided works and that, by some quirk of fate, an incorrect password will result in a corrupted image that ends up on the document. If I don't catch it before this point, lots of documents and money will end up getting thrown away.

My current plan is to create a MD5 checksum to store along with the images. If the decrypted stream's MD5 is different than the original stream, I know the password was incorrect. However, in testing, it appears that an incorrect password results in an exception being thrown.

Is that true 100% of the time? Can I do away with the checksum and just catch an exception?

+1  A: 

With all symmetric key algorithms in .Net the verification occurs at the last block, which contains the padding info (usually PKCS7). If the last decrypted block does not contain a valid padding info, it will be assumed that the data is 'bad' (or the key is 'bad'). For cipher chainning block modes, this method is fairly good, as there is a very low probability of a accidental colission that matches a valid padding info format. For ECB modes things change, but ECB mode is broken anyway and should never be used.

Many applications use 'magic' numbers and text at the start of the data to validate the decrypted content format. 100%, bulletproof accuracy would require that you probably add an HMAC of your own to the data. If you have control over your format, then I'd highly recommend signing the encrypted data.

Remus Rusanu
I have full control of the encrypted image; users can only supply me an unencrypted image and a password used to generate the key and IV for encryption. Do you mean I should concatenate the image prior to encryption with known bytes and then inspect those bytes after decryption?
Will
Yes. If you append the known bytes at front you can validate the key/data combination just by decrypting the first block.
Remus Rusanu
I'm going to try this, thanks.
Will
Urgh, unfortunately I'm not able to do this correctly. I assumed I could just TransformBlock and get out the first 256 bytes (block size is set for 256), but I actually get out less than that. I'll just have to decrypt the entire thing, catch/rewrap crypto exceptions as invalid pass exceptions, then strip off my check block and check it.
Will
block is 8 bytes, 256 **bits**. You should be able to, just make sure you pass 'last block' as false, then you resume from byte 9 onward. I don't have any handy code, I always did this in C/C++ myself using the Crypto API, but I know .net classes support this as well. Also make sure that you resume after first block with the correct key state: the IV and CBC mode will put the key in the right state to decrypt block 2 after you decrypted block 1, you cannot resume with a new key instance unless you decrypt again block 1.
Remus Rusanu
I'd contest that there is a "very low probability" of the padding accidentally being correct. For instance, if the last byte of the decrypted text is 0x01, then it will be considered valid PKCS7 padding; this will occur about 1/256 of the time (the probability of the last two bytes being randomly 0x0202 is only 1/2^16 and of course decreasing for longer padding strings). Since if the key is incorrect, Rijndael should behave exactly like a random mapping, the padding will be considered valid about 1/256 of the time no matter what the original input was.
Jack Lloyd
@Jack: you are correct.
Remus Rusanu
BTW, solution was to prepend the byte array with a known byte array and then remove it after decryption. If the decrypt threw, I knew the password was wrong. If the array was wrong, I knew the password was wrong.
Will
+1  A: 

Encryption doesn't use a password, it uses a key. The decryption doesn't automatically cause an exception when the key is wrong, that would mean that the key could be extracted from the encrypted data, and that would make the encryption pointless. The decryption will always produce a result, but it will be random garbage unless the key is the correct one.

(As Remus pointed out, the padding of the last block can be used to catch incorrect data, but some files doesn't need any padding at all, and sometimes incorrect data may produce something that looks like correct padding.)

Where do you get the exception? Is it when trying to load the decrypted data as an image? In that case you can't rely on that as a 100% safe method to find out if the key was correct, as there is a slim chance that random data may form something that can be loadable as an image.

Storing an MD5 checksum of the original data along with the encrypted data is not a good idea either. That means that you have information about the original data that is not encrypted, and that could be used to crack the key.

Guffa
Rfc2898DeriveBytes is used to create your key and IV from a password and a salt.
Will
Point taken on the MD5. If I do go this route I'll at least switch to SHA512.
Will
@Will: The point is that hashing is not encryption. By keeping unencrypted information about the encrypted data, you are greatly reducing the encryption strength.
Guffa
Yes, I know hashing isn't encryption. I also know there is a slightly greater risk someone with both the AES encrypted file and a SHA512 hash of the same file unencrypted will have a slightly greater chance of decrypting the original, tho I believe that risk might mean ten years off the 200 it would take to crack the encryption. The point is that I have to find SOME WAY to reduce or eliminate the chance a thousand dollars is wasted on a print run because an image was decrypted with the wrong password and no exceptions were thrown.
Will
Going with the check block and dropping the sha hash. You win THIS time, *Guffa*!
Will