C#加密和解密IV问题

C#加密和解密IV问题,c#,encryption,file-io,cryptography,C#,Encryption,File Io,Cryptography,我的代码执行加密,但不执行解密。我想让它生成random IV,所以做了一些更改,但现在它无法解密。我想我搞砸了IV。它似乎不能正确解密 加密文件的前缀不是IV,或者解密方法无法找到IV。我不知道如何解决它。该文件是生成的,例如,我加密了一个文本文件,其中有“hello world”,加密后它生成了一些乱码。解密后生成一个空文本文件 加密方法: private const ushort ITERATIONS = 1300; private static readonly byte[] SALT

我的代码执行加密,但不执行解密。我想让它生成random 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();

}
解密方法:

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

}

必须指定128位(16字节)的块大小才能将加密设置为AES,请参见下面的选项:

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

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

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

  • PaddingMode.如果要加密的数据的最后一个字节是
    0x00
    字节,则零将不起作用。通常使用PKCS#7(古称PKCS#5),PHP mcrypt除外。-多亏了ArtjomB

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

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

    AES.BlockSize = 128;
    
    byte[] iv = new byte[32];
    fsIn.Read(iv, 0, 32)
    
    2:使用32字节的iv(注意这不会产生AES加密):
    更改为加密:

    memStream.Write(AES.IV, 0, 32);  
    
    更改为解密:

    byte[] iv = new byte[32];
    fsIn.Read(iv, 0, 32
    
    基于扎夫回答的结果

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

    fsOut.Write(AES.IV, 0, 32);
    
    并使用(MemoryStream memStream=new MemoryStream(file.Length))
    完全删除该行

    在阅读评论时,这似乎是一个问题:


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

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

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

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

    实际上,我在这里遗漏了什么吗?甚至是IV的一个问题(我想要的是IV应该是随机的,现在解密被破坏了)是由其他原因导致的错误吗?你确定使用128位加密而不是256位吗?Rijndael
    LegalKeySizes[0]。MaxSize
    是256位,而
    LegalBlockSizes[0].MaxSize
    也是256位。后者是不正确的。最好指定所有的值,而不要使用默认值。你不清楚你的“解密问题”。有例外吗?如果是的话,请把它们包括在你的问题中。如果没有,则必须生成一个文件。如果是,该文件与您期望的文件有何不同?如果zaph的答案是正确的,那么前32个字节必须完全不同,文件的其余部分也可以。请确认。该文件是生成的,例如我加密了一个文本文件,其中有“hello world”,加密后它生成了一些乱七八糟的信息。解密后生成一个空文本file@panman再次阅读前两段,其中zaph提供了最佳猜测。关于第二点,我必须使用32字节IV,即使我更改了块大小,我也需要这样做吗?或者选择1或2,我建议选择1。是的,它是有效的。尽管我不得不做出这些改变。。在加密中:“fsOut.Write(AES.IV,0,16);”在解密中:“byte[]IV=新字节[16];fsIn.Read(IV,0,16);”它是有效的还是会破坏随机性?非常感谢你的回答。我在做了更改后会更新结果,它是有效的。尽管我不得不做这些更改。。在加密中:“fsOut.Write(AES.IV,0,16);”在解密中:byte[]IV=新字节[16];fsIn.Read(iv,0,16)它是否有效,或者它是否会破坏IVI随机性的目的假设您进行了更改,因为您设置了
    AES.BlockSize=128。如果是这样的话,那就太好了。非常感谢。顺便说一句,你和扎夫都给了我同样的答案。非常感谢你们。祝你们今天愉快