【问题标题】:C# encryption and decryption IV issueC#加解密IV问题
【发布时间】:2015-12-16 18:57:16
【问题描述】:

我的代码执行加密但不解密。我希望它生成随机 IV,所以做了一些更改,但现在它没有解密。我想我搞砸了IV。它似乎无法正确解密。

IV 没有得到加密文件的前缀,或者解密方法无法找到 IV。我无法理解如何解决它。该文件是生成的,例如我加密了一个带有“hello world”的文本文件,加密后它产生了一些乱码。解密后生成一个空文本文件。

加密方式:

private const ushort ITERATIONS = 1300;
private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

private static byte[] CreateKey(string password, int keySize)
{
    DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, SALT, ITERATIONS);
    return derivedKey.GetBytes(keySize >> 3);
}


public static void EncryptFile(string file, string password)
{

    // First we are going to open the file streams 
    FileStream fsIn = new FileStream(file, FileMode.Open, FileAccess.Read);
    FileStream fsOut = new FileStream(file+"enc", FileMode.OpenOrCreate, FileAccess.Write);

    // Then we are going to derive a Key and an IV from the
    // Password and create an algorithm 
    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);


    // passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    RijndaelManaged AES = new RijndaelManaged();
    AES.KeySize = AES.LegalKeySizes[0].MaxSize;
    AES.BlockSize = AES.LegalBlockSizes[0].MaxSize;
    AES.Padding = PaddingMode.Zeros;

    AES.GenerateIV();
    AES.Key = CreateKey(password, AES.KeySize);
    AES.Mode = CipherMode.CBC;
    using (MemoryStream memStream = new MemoryStream(file.Length)) 
        memStream.Write(AES.IV, 0, 16);

    CryptoStream cs = new CryptoStream(fsOut, AES.CreateEncryptor(), CryptoStreamMode.Write);

    int bufferLen = 4096;
    byte[] buffer = new byte[bufferLen];
    int bytesRead;

    do
    {
        // read a chunk of data from the input file 
        bytesRead = fsIn.Read(buffer, 0, bufferLen);

        // encrypt it 
        cs.Write(buffer, 0, bytesRead);
    } while (bytesRead != 0);

    // close everything 

    // this will also close the unrelying fsOut stream
    cs.Close();
    fsIn.Close();

}

解密方法:

private const ushort ITERATIONS = 1300;
private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

private static byte[] CreateKey(string password, int keySize)
{
    DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, SALT, ITERATIONS);
    return derivedKey.GetBytes(keySize >> 3);
}


public static void DecryptFile(string fileIn, string Password)
{

    // First we are going to open the file streams 
    FileStream fsIn = new FileStream(fileIn,FileMode.Open, FileAccess.Read);

    string extension = System.IO.Path.GetExtension(fileIn);
    string result = fileIn.Substring(0, fileIn.Length - extension.Length);

    FileStream fsOut = new FileStream(result,FileMode.OpenOrCreate, FileAccess.Write);



  //  passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    RijndaelManaged AES = new RijndaelManaged();
    AES.KeySize = AES.LegalKeySizes[0].MaxSize;
    AES.BlockSize = AES.LegalBlockSizes[0].MaxSize;
    AES.Padding = PaddingMode.Zeros;
    byte[] iv = new byte[16];
    fsIn.Read(iv, 0, 16);
    AES.IV=iv;

    AES.Key = CreateKey(Password, AES.KeySize);
    AES.Mode = CipherMode.CBC;


    // Now create a crypto stream through which we are going
    // to be pumping data. 
    // Our fileOut is going to be receiving the Decrypted bytes. 
    CryptoStream cs = new CryptoStream(fsOut,
        AES.CreateDecryptor(), CryptoStreamMode.Write);

    // Now will will initialize a buffer and will be 
    // processing the input file in chunks. 
    // This is done to avoid reading the whole file (which can be
    // huge) into memory. 
    int bufferLen = 4096;
    byte[] buffer = new byte[bufferLen];
    int bytesRead;

    do
    {
        // read a chunk of data from the input file 
        bytesRead = fsIn.Read(buffer, 0, bufferLen);

        // Decrypt it 
        cs.Write(buffer, 0, bytesRead);

    } while (bytesRead != 0);

    // close everything 
    cs.Close(); // this will also close the unrelying fsOut stream 
    fsIn.Close();

}

【问题讨论】:

  • 其实我在这里遗漏了一些东西甚至是 IV 的问题(我想要的只是 IV 应该是随机的,现在解密失败)是由于其他原因引起的错误吗?
  • 您确定使用的是 128 位加密而不是 256 位吗?
  • Rijndael LegalKeySizes[0].MaxSize 是 256 位,LegalBlockSizes[0].MaxSize 也是 256 位。后者是不正确的。最好指定所有值而不使用默认值。
  • 您不清楚您的“解密问题”。有例外吗?如果是这样,请将它们包括在您的问题中。如果不是,则必须生成一个文件。如果是这样,该文件与您预期的文件有何不同?如果 zaph 的回答是正确的,那么前 32 个字节肯定是完全不同的,并且文件的其余部分没问题。请确认。
  • 该文件是生成的,例如我加密了一个文本文件,其中包含“hello world”,加密后它产生了一些乱码。解密后生成一个空文本文件

标签: c# encryption file-io cryptography


【解决方案1】:

您必须指定一个 128 位(16 字节)的块大小才能使用 AES 进行加密,请参阅以下选项:

通常,iv 的大小与块大小相同。您提供的是 16 字节的 iv,而 Rijndael 的最大块大小为 32 字节,因此加密例程很有可能在 iv 之后使用额外的 16 字节垃圾字节。

有一些问题:类是 Rijndael,而 AES 是 Rijndael 的子集,因此 Rijndael 的允许参数可能不适用于 AES。

  1. LegalBlockSizes[0].MaxSize 将返回 256 位的最大 Rijndael 块大小,但 AES 具有 128 位的固定块大小。因此,您实际上并没有使用 AES。您必须指定 128 位(16 字节)的块大小。

  2. 如果要加密的数据的最后一个字节是0x00 字节,PaddingMode.Zeros 将不起作用。通常使用 PKCS#7(古代 PKCS#5),PHP mcrypt 除外。 -- 感谢 ArtjomB。

根据 ArtjomB 的提示。
如果我是正确的,有两个选项,请选择一个选项:

1:根据 AES 要求将块大小更改为 16 字节:
改加解密:

AES.BlockSize = 128;

2:使用 32 字节的 iv(注意这不会产生 AES 加密):
更改为加密:

memStream.Write(AES.IV, 0, 32);  

改解密:

byte[] iv = new byte[32];
fsIn.Read(iv, 0, 32

【讨论】:

  • @panman 再次阅读前两段,zaph 提供了最好的猜测。
  • 关于我必须使用 32 字节 IV 的第二点,即使我更改块大小也需要这样做吗?或者什么
  • 选择1或2,我建议选项1。
  • 是的,它可以工作..尽管我必须进行这些更改..在加密中:“fsOut.Write(AES.IV, 0, 16);"并在解密中:“byte[] iv = new byte[16]; fsIn.Read(iv, 0, 16);”它是有效的还是会破坏IV的随机性目的
【解决方案2】:

基于 zaph 的回答结果...

您需要将 IV 写入文件,而不是写入您从未使用过的临时 MemoryStream:

fsOut.Write(AES.IV, 0, 32);

并彻底删除using (MemoryStream memStream = new MemoryStream(file.Length))这一行。

这似乎是在 cmets 中阅读时的问题:

该文件是生成的,例如我加密了一个具有“hello world”的文本文件,加密后它产生了一些乱码。解密后生成一个空文本文件

这意味着实际的密文是空的,没有什么可以解密的。发生这种情况是因为 IV 实际上并未写入密文文件,因此解密方法认为其中存在的单个块实际上是 IV。

在解密过程中不要忘记从密文文件中读取完整的IV:

byte[] iv = new byte[32];
fsIn.Read(iv, 0, 32)

【讨论】:

  • 非常感谢您的回复,修改后我会更新结果
  • 是的,它可以工作..尽管我必须进行这些更改..在加密中:“fsOut.Write(AES.IV, 0, 16);"并在解密中: byte[] iv = new byte[16]; fsIn.Read(iv, 0, 16) 是有效还是会破坏IV的随机性目的
  • 我假设您已经进行了更改,因为您设置了AES.BlockSize = 128;。在那种情况下,它完全没问题。
  • 非常感谢您和 zaph 都为我提供了相同的答案..非常感谢..祝您有美好的一天
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 2022-01-27
  • 2020-04-07
  • 1970-01-01
  • 2021-11-06
  • 2018-11-28
  • 1970-01-01
相关资源
最近更新 更多