我正在读取一个加密文件,并且想在密码不正确时抛出特定错误。由于使用不正确的密码解密只会成功,但会返回乱码,因此,我想确定密码是否正确。
我这样做的方法是向加密文件写入一个“魔术值”;假设我将值MaGiC
写入文件。每当我解密文件时,我都要确保前5个字节包含MaGiC
。如果不是,则用于解密文件的密码一定是错误的,因此我抛出了InvalidPasswordException
。
MAGICHEADER = new byte[] { 0x4D, 0x61, 0x47, 0x69, 0x43 }; // "MaGiC"
using (var fs = File.OpenRead(path))
using (var algo = ...)
using (var cs = new CryptoStream(fs, algo.CreateDecryptor(), CryptoStreamMode.Read))
{
var hdr = new byte[MAGICHEADER.Length]; // Buffer
cryptoStream.Read(hdr, 0, hdr.Length); // Read magic header from encrypted stream
// If the decrypted data doesn't equal the magic header this means the password is wrong
if (!MAGICHEADER.SequenceEqual(hdr))
throw new InvalidPasswordException();
// ... read rest of file
}
为简洁起见,我遗漏了很多东西(算法,键,IV,块大小,填充,模式等)。对于这种特殊情况,可能很重要:我使用PKCS7
进行填充(但是我很确定所有填充都会导致上述问题)。
现在,在调用方,我编写了如下代码:
try {
var data = ReadFile("C:\foo\bar", "INCORRECTPASSWORD");
// ... Do stuff with data
} catch (InvalidPasswordException) {
// Oh noes! Wrong password! Ask to enter password again
}
但是,令我惊讶的是InvalidPasswordException
没有被发现;我没有,但类型为CryptographicException
:填充无效,无法删除。。
当我调试问题时,我看到抛出了InvalidPasswordException
。 我还认为我知道发生了什么事情:抛出InvalidPasswordException
,using
导致CryptoStream
被处置,而CryptoStream
确定因为我们不在流的末尾,所以剩下的必须是“填充”,这会导致异常。或至少符合这些原则。
要“强制”抛出我的异常,我可以执行以下操作:
if (!MAGICHEADER.SequenceEqual(hdr)) {
try { cs.FlushFinalBlock(); } catch { } // YUK!!
throw new InvalidPasswordException();
}
或者:
if (!MAGICHEADER.SequenceEqual(hdr)) {
try { cs.Dispose(); } catch { } // YUK!!
throw new InvalidPasswordException();
}
但是,我一直希望有一个更优雅的解决方案。有没有一种方法可以初始化CryptoStream
而不丢弃?还是一种干净地“关闭” CryptoStream
而不会抛出的方法?我已经尝试过Close()
,Clear()
等,但是所有这些方法也会导致CryptoGraphicException
迫使我在抛出自己的{{1 }}。