C# Pass Encrypted Data from a file into a memory stream. Throwing exception
Sort of a continuation from an earlier question, done a bunch of googling, but I don't think I'm on the right path here. Essentially what I'm doing is opening a file with a segment of code like so:
byte[] cipherText = File.ReadAllBytes(encryptedFile);
Then passing th开发者_如何转开发at byte array to a static bool method which calls the actual decryption method and returns true if no cryptographic exception is thrown. On my main form, I've got a button click which calls the static bool from an If statement, unlocking some additional interface items if it comes back true, and popping up an "invalid password" messagebox if it returns false.
I stepped through the entire process, and its throwing a "Padding is invalid and cannot be removed." exception, which some searching has revealed that should happen when the wrong decryption key is supplied. I know I'm passing the same key, at least I'm typing it in, so I'm assuming the problem has to do with either the way the byte[] is passed, or with the streams themselves. Here's an example of the decryption method:
public static string Decrypt(string password, byte[] ciphertext)
{
byte[] key, iv;
CreateKeyIV(password, out key, out iv);
using (MemoryStream encrypted = new MemoryStream(ciphertext))
using (CryptoStream dec = new CryptoStream(encrypted, _algorithm.CreateDecryptor(key, iv), CryptoStreamMode.Read))
using (StreamReader reader = new StreamReader(dec))
{
try
{
return reader.ReadToEnd();
}
catch (CryptographicException)
{
throw new CryptographicException("Invalid password.");
}
finally
{
encrypted.Close();
dec.Close();
reader.Close();
}
}
}
Does anyone see what I'm missing?
The decryption code looks okay to me, although I would make a few changes:
- Why not pass in a stream or filename rather than the encrypted data as a byte array? There's no need to read it all in one go, then wrap the data in a
MemoryStream
when you can just use aFileStream
as the source - You don't need to call
Close
- that's what the using statements are for - Why are you throwing a new
CryptographicException
which have miscommunicated the problem, rather than letting the existing exception bubble up?
In other words, this is what my method would look like:
public static string Decrypt(string password, Stream encrypted)
{
byte[] key, iv;
CreateKeyIV(password, out key, out iv);
using (CryptoStream dec = new CryptoStream(encrypted,
_algorithm.CreateDecryptor(key, iv), CryptoStreamMode.Read))
using (StreamReader reader = new StreamReader(dec))
{
return reader.ReadToEnd();
}
}
However, you've shown what's wrong in another comment:
// Bad code! Do not use!
byte[] newCipher = Encryptor.Encrypt(newPass, newPass);
string writeIt = System.Text.Encoding.UTF8.GetString(newCipher);
You're mangling your encrypted data. The result of encryption will rarely be a valid UTF-8 string. You should just write it straight to disk in binary, never trying to convert it to text. Either your encryption code could do this directly, or you could call File.WriteAllBytes()
. If you absolutely have to convert arbitrary binary data to a string at some point, you should convert it to base64, using Convert.ToBase64String(data)
. That way you can definitely get at the same binary data later on by converting it back again. Trying to "decode" it as text is an almost sure-fire way to lose data - as you're doing here.
I won't open a StreamReader
on a CryptoStream
, but actually I will call directly the method Read
of the class CryptoStream
, passing a byte[]
buffer that it will hold the decrypted data.
Here is a snippet of mine. I've used it with MemoryStream and FileStream.
Stream s = ...;
ICryptoTransform cTransform = Encryptor.CreateDecryptor();
using (CryptoStream cStream = new CryptoStream(s, cTransform, CryptoStreamMode.Read)) {
byte[] mCryptoData = new byte[s.Length];
cStream.Read(mCryptoData, 0, mCryptoData.Length);
...
精彩评论