C#加密解密IV问题

本文关键字:问题 IV 加密解密 | 更新日期: 2023-09-27 18:27:07

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

IV没有被加在加密文件的前缀上,或者解密方法找不到IV。我不明白如何解决它。文件是产生的。例如,我加密了一个有"你好世界"的文本文件,加密后产生了一些胡言乱语。解密后,它生成了一个空文本文件。

加密方法:

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();
}

C#加密解密IV问题

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

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

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

  1. CCD_ 1将返回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

在zaph答案的基础上

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

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

并完全移除线路using (MemoryStream memStream = new MemoryStream(file.Length))

这似乎是阅读评论时的问题:

例如,我加密了一个带有"你好世界"的文本文件,加密后产生了一些胡言乱语。解密后,它产生了一个空的文本文件

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

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

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