如何在使用CryptoStream时抛出而不引起CryptoGraphicException

时间:2018-08-09 13:36:16

标签: c# io cryptography cryptostream

我正在读取一个加密文件,并且想在密码不正确时抛出特定错误。由于使用不正确的密码解密只会成功,但会返回乱码,因此,我想确定密码是否正确。

我这样做的方法是向加密文件写入一个“魔术值”;假设我将值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我还认为我知道发生了什么事情:抛出InvalidPasswordExceptionusing导致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 }}。

0 个答案:

没有答案